BARBEY Charlotte et PRUTKI Lucas

M2 MoSEF - Data Mining

Exploratory Data Analysis (EDA)

Librairies

# Définir les librairies que l'on va utiliser
libraries_used <- 
  c("dplyr", "funModeling", "ggplot2", "PerformanceAnalytics", "plotly", "scales", "tidyr", "tinytex")

# Vérification des librairies installées
libraries_missing <- 
  libraries_used[!(libraries_used %in% installed.packages()[,"Package"])]

# Installer les librairies manquantes
if(length(libraries_missing)) install.packages(libraries_missing)

Import des librairies


library(dplyr)
library(funModeling)
library(ggplot2)
library(PerformanceAnalytics)
library(plotly)
library(scales)
library(tidyr)
library(tinytex)

Import des données

data <- read.csv("data_lending_club.csv")

La base de données comporte 151 variables. Nous n’allons pas toutes les regarder une par une. en revanche, nous allons cibler les plus intéressantes et celles qui sont susceptibles d’expliquer au mieux pourquoi un investisseur pourrait faire un défaut de paiement. En outre, l’exploration graphique de la base de données vise aussi à comprendre nos données, à savoir comment les nettoyer mais aussi et surtout à émettre nos premières hypothèses quant à la problématique posée par le projet d’investissement.

Description des données

structure_data <- str(data)
'data.frame':   20000 obs. of  151 variables:
 $ id                                        : int  68407277 68355089 68341763 66310712 68476807 68426831 68476668 67275481 68466926 68616873 ...
 $ member_id                                 : logi  NA NA NA NA NA NA ...
 $ loan_amnt                                 : num  3600 24700 20000 35000 10400 ...
 $ funded_amnt                               : num  3600 24700 20000 35000 10400 ...
 $ funded_amnt_inv                           : num  3600 24700 20000 35000 10400 ...
 $ term                                      : chr  " 36 months" " 36 months" " 60 months" " 60 months" ...
 $ int_rate                                  : num  14 12 10.8 14.8 22.4 ...
 $ installment                               : num  123 820 433 830 290 ...
 $ grade                                     : chr  "C" "C" "B" "C" ...
 $ sub_grade                                 : chr  "C4" "C1" "B4" "C5" ...
 $ emp_title                                 : chr  "leadman" "Engineer" "truck driver" "Information Systems Officer" ...
 $ emp_length                                : chr  "10+ years" "10+ years" "10+ years" "10+ years" ...
 $ home_ownership                            : chr  "MORTGAGE" "MORTGAGE" "MORTGAGE" "MORTGAGE" ...
 $ annual_inc                                : num  55000 65000 63000 110000 104433 ...
 $ verification_status                       : chr  "Not Verified" "Not Verified" "Not Verified" "Source Verified" ...
 $ issue_d                                   : chr  "Dec-2015" "Dec-2015" "Dec-2015" "Dec-2015" ...
 $ loan_status                               : chr  "Fully Paid" "Fully Paid" "Fully Paid" "Current" ...
 $ pymnt_plan                                : chr  "n" "n" "n" "n" ...
 $ url                                       : chr  "https://lendingclub.com/browse/loanDetail.action?loan_id=68407277" "https://lendingclub.com/browse/loanDetail.action?loan_id=68355089" "https://lendingclub.com/browse/loanDetail.action?loan_id=68341763" "https://lendingclub.com/browse/loanDetail.action?loan_id=66310712" ...
 $ desc                                      : chr  "" "" "" "" ...
 $ purpose                                   : chr  "debt_consolidation" "small_business" "home_improvement" "debt_consolidation" ...
 $ title                                     : chr  "Debt consolidation" "Business" "" "Debt consolidation" ...
 $ zip_code                                  : chr  "190xx" "577xx" "605xx" "076xx" ...
 $ addr_state                                : chr  "PA" "SD" "IL" "NJ" ...
 $ dti                                       : num  5.91 16.06 10.78 17.06 25.37 ...
 $ delinq_2yrs                               : num  0 1 0 0 1 0 0 1 0 0 ...
 $ earliest_cr_line                          : chr  "Aug-2003" "Dec-1999" "Aug-2000" "Sep-2008" ...
 $ fico_range_low                            : num  675 715 695 785 695 690 680 705 685 700 ...
 $ fico_range_high                           : num  679 719 699 789 699 694 684 709 689 704 ...
 $ inq_last_6mths                            : num  1 4 0 0 3 0 0 0 1 0 ...
 $ mths_since_last_delinq                    : num  30 6 NA NA 12 NA 49 3 NA 75 ...
 $ mths_since_last_record                    : num  NA NA NA NA NA NA NA NA 106 NA ...
 $ open_acc                                  : num  7 22 6 13 12 5 12 8 14 8 ...
 $ pub_rec                                   : num  0 0 0 0 0 0 0 0 1 0 ...
 $ revol_bal                                 : num  2765 21470 7869 7802 21929 ...
 $ revol_util                                : num  29.7 19.2 56.2 11.6 64.5 68.4 84.5 5.7 34.5 39.1 ...
 $ total_acc                                 : num  13 38 18 17 35 6 27 15 23 18 ...
 $ initial_list_status                       : chr  "w" "w" "w" "w" ...
 $ out_prncp                                 : num  0 0 0 15898 0 ...
 $ out_prncp_inv                             : num  0 0 0 15898 0 ...
 $ total_pymnt                               : num  4422 25680 22706 31464 11740 ...
 $ total_pymnt_inv                           : num  4422 25680 22706 31464 11740 ...
 $ total_rec_prncp                           : num  3600 24700 20000 19102 10400 ...
 $ total_rec_int                             : num  822 980 2706 12362 1340 ...
 $ total_rec_late_fee                        : num  0 0 0 0 0 0 0 0 0 0 ...
 $ recoveries                                : num  0 0 0 0 0 0 0 0 0 0 ...
 $ collection_recovery_fee                   : num  0 0 0 0 0 0 0 0 0 0 ...
 $ last_pymnt_d                              : chr  "Jan-2019" "Jun-2016" "Jun-2017" "Feb-2019" ...
 $ last_pymnt_amnt                           : num  123 926 15813 830 10129 ...
 $ next_pymnt_d                              : chr  "" "" "" "Apr-2019" ...
 $ last_credit_pull_d                        : chr  "Mar-2019" "Mar-2019" "Mar-2019" "Mar-2019" ...
 $ last_fico_range_high                      : num  564 699 704 679 704 759 654 674 719 679 ...
 $ last_fico_range_low                       : num  560 695 700 675 700 755 650 670 715 675 ...
 $ collections_12_mths_ex_med                : num  0 0 0 0 0 0 0 0 0 0 ...
 $ mths_since_last_major_derog               : num  30 NA NA NA NA NA NA 3 NA 75 ...
 $ policy_code                               : num  1 1 1 1 1 1 1 1 1 1 ...
 $ application_type                          : chr  "Individual" "Individual" "Joint App" "Individual" ...
 $ annual_inc_joint                          : num  NA NA 71000 NA NA NA NA NA NA NA ...
 $ dti_joint                                 : num  NA NA 13.8 NA NA ...
 $ verification_status_joint                 : chr  "" "" "Not Verified" "" ...
 $ acc_now_delinq                            : num  0 0 0 0 0 0 0 0 0 0 ...
 $ tot_coll_amt                              : num  722 0 0 0 0 ...
 $ tot_cur_bal                               : num  144904 204396 189699 301500 331730 ...
 $ open_acc_6m                               : num  2 1 0 1 1 0 0 0 2 0 ...
 $ open_act_il                               : num  2 1 1 1 3 1 2 3 1 2 ...
 $ open_il_12m                               : num  0 0 0 0 0 0 0 0 0 2 ...
 $ open_il_24m                               : num  1 1 4 1 3 0 2 4 0 3 ...
 $ mths_since_rcnt_il                        : num  21 19 19 23 14 338 18 13 35 10 ...
 $ total_bal_il                              : num  4981 18005 10827 12609 73839 ...
 $ il_util                                   : num  36 73 73 70 84 99 63 75 57 72 ...
 $ open_rv_12m                               : num  3 2 0 1 4 0 2 0 2 0 ...
 $ open_rv_24m                               : num  3 3 2 1 7 0 3 0 7 2 ...
 $ max_bal_bc                                : num  722 6472 2081 6987 9702 ...
 $ all_util                                  : num  34 29 65 45 78 76 74 55 46 49 ...
 $ total_rev_hi_lim                          : num  9300 111800 14000 67300 34000 ...
 $ inq_fi                                    : num  3 0 2 0 2 0 1 1 2 0 ...
 $ total_cu_tl                               : num  1 0 5 1 1 0 0 0 0 0 ...
 $ inq_last_12m                              : num  4 6 1 0 3 0 1 2 1 1 ...
 $ acc_open_past_24mths                      : num  4 4 6 2 10 0 6 4 7 5 ...
 $ avg_cur_bal                               : num  20701 9733 31617 23192 27644 ...
 $ bc_open_to_buy                            : num  1506 57830 2737 54962 4567 ...
 $ bc_util                                   : num  37.2 27.1 55.9 12.1 77.5 ...
 $ chargeoff_within_12_mths                  : num  0 0 0 0 0 0 0 0 0 0 ...
 $ delinq_amnt                               : num  0 0 0 0 0 0 0 0 0 0 ...
 $ mo_sin_old_il_acct                        : num  148 113 125 36 128 338 142 149 164 155 ...
 $ mo_sin_old_rev_tl_op                      : num  128 192 184 87 210 54 306 55 129 253 ...
 $ mo_sin_rcnt_rev_tl_op                     : num  3 2 14 2 4 32 10 32 1 15 ...
 $ mo_sin_rcnt_tl                            : num  3 2 14 2 4 32 10 13 1 10 ...
 $ mort_acc                                  : num  1 4 5 1 6 0 4 3 1 1 ...
 $ mths_since_recent_bc                      : num  4 2 101 2 4 36 12 32 4 50 ...
 $ mths_since_recent_bc_dlq                  : num  69 NA NA NA 12 NA NA NA NA NA ...
 $ mths_since_recent_inq                     : num  4 0 10 NA 1 NA 10 8 1 10 ...
 $ mths_since_recent_revol_delinq            : num  69 6 NA NA 12 NA NA NA NA NA ...
 $ num_accts_ever_120_pd                     : num  2 0 0 0 0 0 0 1 0 1 ...
 $ num_actv_bc_tl                            : num  2 5 2 4 4 2 4 2 6 3 ...
 $ num_actv_rev_tl                           : num  4 5 3 5 6 3 6 2 9 3 ...
 $ num_bc_sats                               : num  2 13 2 8 5 2 4 3 7 3 ...
 $ num_bc_tl                                 : num  5 17 4 10 9 2 5 3 10 6 ...
 $ num_il_tl                                 : num  3 6 6 2 10 2 7 9 3 5 ...
  [list output truncated]
structure_data
NULL

Précédemment, nous avons pris une vue très globale de la base de données afin d’en avoir un premier aperçu. On va donc pouvoir regarder avec plus de détails les différentes variables qui la composent et de commencer à l’explorer. La cellule, ci-dessous, nous renvoie de nombreuses informations : - Nombre de valeurs nulles par variable ainsi que les proportions en pourcentage ; - Nombre de valeurs manquantes par variable ainsi que les proportions en pourcentage ; - Nombre de valeurs infinies par variable ainsi que les proportions en pourcentage ; - Le type de chaque variable ; - Le nombre de valeurs uniques que comporte chaque variable.

La première chose que l’on voit c’est qu’il ne semble pas avoir de doublons dans la base. Pourquoi ? la variable “Id” qui représente l’identifiant unique Lending Club de chaque emprunteur de la plateforme possède 20 000 valeurs uniques, soit une par client. On remarque, également, que des variables possèdent 100% de valeurs manquantes (à l’instar de la caractéristique “revol_bal_joint”) ou des proportions très poches de 100% (la variable “annual_inc_joint” avec 99.25% de valeurs manquantes). À l’inverse d’autres variables affichent, heuresement, 0% de valeurs manquantes. Il y aura donc un gros travail à réaliser sur le traitement et la gestion des valeurs manquantes dans la base de données.

Description des données

detailled_data <- df_status(data, print_results = FALSE)
detailled_data

Afficher le nombre de valeurs uniques au sein des différentes caractéristiques est très informatif car cela nous donne une information sur la qualité informative de la caractéristique eu égard à l’évènement à modéliser. Illustrons nos propos, si une caractéristique affiche une proportion de 0% de valeurs uniques cela signifie que toutes les observations ont exactement la même valeur pour cette variable. Cette dernière ne permet pas de discriminer les individus pour le phénomène étudié et de facto ne véhicule aucune valeur d’information. Elle pourra être supprimée pour la future analyse. En revanche une variable telle que le revenu annuel d’un emprunteur qui dispose de 100% de valeurs uniques (i.e. soit de 20 000 valeurs différentes) signifie que chaque client possède un revenu différent ce qui peut permettre de discriminer ces clients par rapport à notre variable cible à modéliser. Cette caractéristique peut donc être conservée pour la modélisation future. Il s’agit donc d’identifier ces variables qui sont des constantes afin de les supprimer et éviter tout biais dans notre analyse. Exemple : “member_id”, “issue_d”, “policy_code”, “hardship_length”, “deferral_term”, “next_pymnt_d”, etc.

Description des données

detailled_data <-
  detailled_data %>%
  mutate(uniq_rate = unique / nrow(data))

detailled_data %>%
  select(variable, unique, uniq_rate) %>%
  mutate(unique = unique, uniq_rate = percent(uniq_rate))

Définition des fonctions

# Plot des subplots composé d'un histogram et d'un boxplot
plot_subplot <- function(data, column, bycolumn, title, xtitle, ytitle) {
  
    fig1 <- plot_ly(data, x = ~column, type = "histogram", marker = list(color = "#BCA9F5"))
    fig2 <- plot_ly(data, y = ~column, x = ~bycolumn, type = "box", marker = list(color = "#82caaf"))
    
    fig <- subplot(fig1, fig2, nrows = 2) 
    fig <- fig %>% layout(title = title,
             plot_bgcolor="#FFFFFF", 
             xaxis = list(
               title = xtitle,
               zerolinecolor = "#ffff", 
               zerolinewidth = 2, 
               gridcolor = "ffff"), 
             yaxis = list(
               title = ytitle,
               zerolinecolor = "#ffff", 
               zerolinewidth = 2, 
               gridcolor = "ffff"), 
             showlegend = FALSE,
             showlegend2 = FALSE)
    
    return(fig)
}

# Plot un histogram
plot_histogram <- function(data, column, title, xtitle, ytitle) {
  
    fig <- plot_ly(data, x = ~column, type = "histogram", marker = list(color = '#82caaf'))
    fig <- fig %>% layout(title = title,
             xaxis = list(title = xtitle),
             yaxis = list(title = ytitle))
    
    return(fig)
}

# Plot un boxplot
plot_box <- function(data, column, bycolumn, title, xtitle, ytitle) {
  
    fig <- plot_ly(data, y = ~column, x = ~bycolumn, type = "box", marker = list(color = "#BCA9F5"))
    fig <- fig %>% layout(title = title,
             xaxis = list(title = xtitle),
             yaxis = list(title = ytitle))
    
    return(fig)
}

# Calculer les statistiques descriptives
descriptive_statistic1 <- function(data, column) {
  
    data %>%
      summarise(Minimum = round(min(column), digits = 4),
                Maximum = round(max(column), digits = 4),
                Moyenne = round(mean(column), digits = 4),
                Mediane = round(median(column), digits = 4),
                Variance = round(var(column), digits = 4),
                Volatilite = round(sd(column), digits = 4),
                Kurtosis = round(kurtosis(column), digits = 4),
                Skewness = round(skewness(column), digits = 4))
}

# Calculer les statistiques descriptives
descriptive_statistic2 <- function(data, column) {
    data %>%
      group_by(data$loan_status) %>%
      summarise(Minimum = round(min(column), digits = 4),
                Maximum = round(max(column), digits = 4),
                Moyenne = round(mean(column), digits = 4),
                Mediane = round(median(column), digits = 4),
                Variance = round(var(column), digits = 4),
                Volatilite = round(sd(column), digits = 4),
                Kurtosis = round(kurtosis(column), digits = 4),
                Skewness = round(skewness(column), digits = 4))
}

# Plot un boxplot
give_count <- 
  stat_summary(fun.data = function(x) return(c(y = median(x)*1.06,
                                               label = length(x))),
               geom = "text")

give_mean <- 
  stat_summary(fun.y = mean, colour = "darkgreen", geom = "point", 
               shape = 18, size = 3, show.legend = FALSE)
`fun.y` is deprecated. Use `fun` instead.
ggplot_box <- function(data, xcolumn, ycolumn, bycolumn, title, xtitle, ytitle) {
  
  data %>%
    ggplot(aes(xcolumn, ycolumn)) +
    geom_boxplot(fill = "white", colour = "darkblue", 
                 outlier.colour = "red", outlier.shape = 1) +
    give_count +
    give_mean +
    scale_y_continuous(labels = comma) +
    labs(title = title, x = xtitle, y = ytitle) +
    facet_wrap(bycolumn)
}

EDA

L'objectif de notre EDA n'est pas de visualiser les 151 caractéristiques de notre base de données. Sur la base du dictionnaire de données, de nos connaissances sur le risque de crédit et des recherches que nous avons pu faire, nous allons exposer une analyse graphique et statistique sur les caractéristiques qui nous paraissent les plus importantes dans l'augmentation de la probabilité de défaut. Voici une liste non exhaustive : - L’historique des remboursements du client, s’il les règlent à temps ou non ; - De la signalétique personne (lieu de résidence, locataire ou propriétaire, catégorie socio-professionnelle, etc.) ; - Le montant total dû et la mensualité associée ; - Le solde courant ; - Quand la première ligne de crédit a-t-elle été ouverte ; - Le type de ligne de crédit (revolving, hypothécaire) ; - La raison pour laquelle l’emprunt est contracté ; - Le nombre de lignes de crédit récemment ouverte et le nombre total de lignes que l’emprunteur possède ; - Le nombre de lignes de crédit ouvertes qui sont en défaut ou grave retard de paiement ; - Le nombre d’enquêtes de crédit récentes ; - La note de crédit attribuée au dossier.

Variable : “loan_status”

Il s’agit du statut actuel du prêt. Cette caractéristique prend plusieurs modalités : un prêt remboursé ou non, en cours ou en retard de paiement.

Observation : on remarque que beaucoup de prêts, dans l’historique de la plateforme, sont totalement remboursés (70.8%). Ensuite il y a une part non négligeable de prêts qui sont en cours de remboursement (10.6%) mais qui sont, à l’heure, non remboursés, puis quand même une bonne part de prêts qui ne sont pas du tout remboursés (17.9%). Il y a une très faible minorité de paiements qui sont en retard ou en grâce de paiement (moins de 1%).

Intuition : cette variable sera la cible de nos modèles de Machine Learning supervisés. Il va donc falloir restructurer cette caractéristique afin d’avoir un problème de classification à 2 classes. Notre définition du défaut de paiement est la suivante : si l’emprunteur ne rembourse pas à la date d’échéance fixée par le contrat alors il est considéré en défaut de paiement, et cela même s’il rembourse plusieurs jours / mois / années après. Ainsi, d’un côté nous aurons les emprunteurs ayant totalement remboursés leur emprunt et de l’autre, les emprunteurs restants. À priori, les individus en grâce seront exclus de la modélisation car ils n’ont pas remboursé mais sont en période de grâce. En outre, pour les emprunts en cours de remboursement nous ne savons pas si l’emprunteur est en retard ou non. Ainsi, nous les écarteront également.

data %>% count(loan_status)
fig <- plot_ly(data, labels = ~loan_status, type = "pie", marker = list(colors = c("#BCA9F5", "#82caaf", "#A9BCF5", "#A9F5BC", "#F7BE81", "#F78181")))
fig <- fig %>% layout(title = "Loan status proportion",
         xaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE),
         yaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE))

fig

Pour continuer notre data visualisation, nous allons créer un prédicat nommé “default” qui nous permettra de visualiser plus en détail nos différentes caractéristiques en opposant les emprunteurs en situation de défaillance et nos emprunteurs sains. Ce ré-encodage se base sur notre intuition précédente.

data2 <- data

data2 <- data2[!(data2$loan_status == "Current" | data2$loan_status == "In Grace Period"), ]

default_var <- c("Charged Off", "Late (16-30 days)", "Late (31-120 days)")

data2 <- data2 %>%
  mutate(default = ifelse(!(loan_status %in% default_var), FALSE, TRUE))

data2 %>%
  summarise(default_freq = sum(default / n()))

Nous pouvons voir que suite à cette définition et classification du défaut de paiement, 21% des emprunteurs de notre base de données sont considérés en défaut de paiement tandis que 79% sont des emprunteurs sains.

table(data2$default) / nrow(data2)

    FALSE      TRUE 
0.7934301 0.2065699 

Variable : “loan_amount”

Il s’agit du montant du prêt demandé par l’emprunteur sur la plateforme.

Observation : dans l’ensemble, la distribution des montants des prêts demandés est légèrement étalée vers la droite. Ceci indique une skewness positive (0.61), donc une moyenne supérieure à la médiane. - 50% des prêts accordés sont d’un montant inférieur à 14 000€ (valeur de la médiane). - Les montants des demandes de financement connaissent un pic autour de chaque pallier de 5 000€. - De manière générale, les prêts totalement remboursés sont ceux pour lesquels le montant moyen du prêt est le plus faible. Pour toutes les autres modalités du statut du prêt, les montants moyens de financement sont supérieurs. - Pas d’outliers pour cette variable. Les montants minimums et maximums sont tout à fait normaux et plutôt équilibrés entre les différentes classes. Sauf pour deux classes. Ceci peut venir du fait qu’il s’agisse d’un sample et non du dataset total.

fig <- plot_subplot(data, data$loan_amnt, data$loan_status, "Loan amount variable", "Loan amount", "Count")
fig
stat_desc <- descriptive_statistic1(data, data$loan_amnt)
stat_desc
data %>%
  group_by(loan_status) %>%
  summarise(Minimum = round(min(loan_amnt), digits = 4),
            Maximum = round(max(loan_amnt), digits = 4),
            Moyenne = round(mean(loan_amnt), digits = 4),
            Mediane = round(median(loan_amnt), digits = 4),
            Variance = round(var(loan_amnt), digits = 4),
            Ecart_type = round(sd(loan_amnt), digits = 4),
            Kurtosis = round(kurtosis(loan_amnt), digits = 4),
            Skewness = round(skewness(loan_amnt), digits = 4))
`summarise()` ungrouping output (override with `.groups` argument)

Quid du montant octroyé pour le prêt en fonction du grade de crédit Lending Club de l’emprunteur ? Nous remarquons que le montant octroyé a tendance à augmenter avec les grades de crédit Lending Club. C’est-à-dire que les grades les plus risqués sont ceux qui empruntent les plus grands montants en moyenne. Pour la distinction entre les classes, on voit que les emprunteurs en défaut (TRUE) empruntent, en moyenne, très légèrement plus que les emprunteurs sains (FALSE).

ggplot_box(data2, data2$grade, data2$loan_amnt, data2$default, "Loan Amount by Grade", "Grade", "Loan amount \n")

Quid du montant octroyé pour le prêt en fonction de la vérification de la source de revenu annuel de l’emprunteur ? Les montants moyens empruntés sont relativement proches pour les trois modalités de la variable “verification_status”.

data2 %>%
  ggplot(aes(verification_status, loan_amnt)) +
  geom_boxplot(fill = "white", colour = "darkblue", 
               outlier.colour = "red", outlier.shape = 1) +
  give_count +
  give_mean +
  scale_y_continuous(labels = comma) +
  labs(title="Loan Amount by verification status", x = "Verification status", y = "Loan amount \n")

Variable : “funded_amnt”

COMMENTAIRES.

fig <- plot_subplot(data, data$funded_amnt, data$loan_status, "Funded amount variable", "Funded amount", "Count")
fig
stat_desc <- descriptive_statistic1(data, data$funded_amnt)
stat_desc
data %>%
  group_by(loan_status) %>%
  summarise(Minimum = round(min(funded_amnt), digits = 4),
            Maximum = round(max(funded_amnt), digits = 4),
            Moyenne = round(mean(funded_amnt), digits = 4),
            Mediane = round(median(funded_amnt), digits = 4),
            Variance = round(var(funded_amnt), digits = 4),
            Ecart_type = round(sd(funded_amnt), digits = 4),
            Kurtosis = round(kurtosis(funded_amnt), digits = 4),
            Skewness = round(skewness(funded_amnt), digits = 4))
`summarise()` ungrouping output (override with `.groups` argument)

Variable : “int_rate”

C’est le taux d’intérêt du prêt.

Observation : globalement, le taux d’intérêt varie de 5% à 30% avec une distribution plutôt centrée à gauche mais bien étalée à droite (skewness positive : 0.65). On remarque un pic aux alentours de 12/13% (moyenne à 12.2%) puis une décroissance lente jusqu’au maximum de la distribution (28.99%). Quid des différences inter-classe ? Les taux d’intérêt restent relativement proche néanmoins la moyenne des emprunteurs ayant remboursés est de 11.4% tandis que celle des défaillants ainsi que des autres classes elle est supérieure à 14%. Nous retrouvons cette même distinction au niveau de la médiane des distributions. Ceci est plutôt logique, si le risque de défaut augmente, le taux d’intérêt augmente aussi. Nous concluons que, en moyenne, plus le taux d’intérêt est fort, moins le prêt est remboursé.

Notons qu’il semble y avoir plusieurs outliers pour les trois premières classes du graphique. Cela sera à traiter dans le pre-processing.

Ci-dessous, nous allons analyser avec de nombreux détails cette variable qui est une réelle mesure de risque en économie monétaire et bancaire.

fig <- plot_subplot(data, data$int_rate, data$loan_status, "Interest rate variable", "Interest rate", "Count")
fig
stat_desc <- descriptive_statistic1(data, data$int_rate)
stat_desc
data %>%
  group_by(loan_status) %>%
  summarise(Minimum = round(min(int_rate), digits = 4),
            Maximum = round(max(int_rate), digits = 4),
            Moyenne = round(mean(int_rate), digits = 4),
            Mediane = round(median(int_rate), digits = 4),
            Variance = round(var(int_rate), digits = 4),
            Ecart_type = round(sd(int_rate), digits = 4),
            Kurtosis = round(kurtosis(int_rate), digits = 4),
            Skewness = round(skewness(int_rate), digits = 4))
`summarise()` ungrouping output (override with `.groups` argument)

Quid de l’évolution des taux d’intérêt selon l’échéance du prêt et le grade de crédit Lending Club de l’emprunteur ? C’est assez perturbant, nous nous attendions à observer une plus forte distinction entre les différentes échéances. En effet, en économie logiquement plus l’horizon temporelle (i.e. ici l’échéance) est lointain et plus les risques sont élevés. Ainsi, normalement le taux d’intérêt à long-terme (60 mois) devrait être supérieur à celui d’un emprunt sur 36 mois car le risque de défaut est plus grand à long-terme. Globalement, les taux d’intérêt sont similaires pour chaque grade de crédit et cela peu importe l’échéance du prêt.

data2 %>%
  ggplot(aes(grade, int_rate)) +
  geom_boxplot(fill = "white", colour = "darkblue", 
               outlier.colour = "red", outlier.shape = 1) +
  give_count +
  give_mean +
  scale_y_continuous(labels = comma) +
  labs(title="Interest Rate by Grade", x = "Grade", y = "Interest Rate \n") +
  facet_wrap(~ term)

Taux d’intérêt moyen et médian par grade de crédit Lending club

data2 %>%
  select(int_rate, grade) %>%
  group_by(grade) %>%
  summarise(int_rate_mean = mean(int_rate, na.rm = TRUE),
            int_rate_median = median(int_rate, na.rm = TRUE),
            n = n())
`summarise()` ungrouping output (override with `.groups` argument)

Quid du taux d’intérêt selon le statut de l’emprunteur et sa caractéristique “owner_ship” ? La première chose à relever est le fait que les taux d’intérêt sont bien plus élevés pour les emprunteurs en situation de défaillance. Dans un second temps, nous remarquons que les emprunteurs qui sont en location immobilière possèdent un taux d’intérêt moyen plus élevé que ceux étant propriétaires ou en hypothèques. De manière économique cela peut être cohérent car la banque ou l’investisseur sait que si cet emprunteur ne peut rembourser son emprunt, il possède toujours un certain patrimoine pouvant servir à rembourser ses dettes.

ggplot_box(data2, data2$home_ownership, data2$int_rate, data2$default, "Interest Rate by Home Ownership", "Home Ownership", "Interest rate \n")

Quid du taux d’intérêt selon le statut de l’emprunteur et sa caractéristique “purpose” ? Comme précédemment, nous notons que les taux d’intérêt moyens sont légèrement plus élevés pour les clients en défaut de paiement. La question que l’on se pose ici est de savoir si le fait d’emprunter pour un objectif précis augmente la probabilité de faire défaut et surtout si, pour un même objectif d’investissement, il y a une différence inter-classe. C’est bien ce que nous remarquons. Le fait d’emprunter pour la modalité “small business” (emprunt pour la création et le lancement de son entreprise) accroît très fortement le taux d’intérêt et cela peu importe le pattern de défaut ou non de l’emprunteur. Le second motif faisant accroître le taux d’intérêt est l’emprunt pour l’achat d’une maison. Ceci est cohérent avec le fait que nous avions vu que les clients locataires avaient un taux d’intérêt plus élevé sachant que c’est potentiellement ces derniers qui contractent des prêts pour l’achat d’une maison.

Globalement nous remarquons bien que les taux d’intérêt possèdent un potentiel pouvoir discriminant afin de différencier les emprunteurs en défaut ou non.

data2 %>%
  ggplot(aes(purpose, int_rate)) +
  geom_boxplot(fill = "white", colour = "darkblue", 
               outlier.colour = "red", outlier.shape = 1) +
  give_count +
  give_mean +
  scale_y_continuous(labels = comma) +
  facet_wrap(~ default) +
  theme(axis.text.x = element_text(angle = 45))

  labs(title="Interest Rate by Loan Purpose", x = "Loan purpose", y = "Interest rate \n")
$x
[1] "Loan purpose"

$y
[1] "Interest rate \n"

$title
[1] "Interest Rate by Loan Purpose"

attr(,"class")
[1] "labels"

Quid du taux d’intérêt selon le statut de l’emprunteur et sa caractéristique “verification_status” ? Comme précédemment, le taux d’intérêt augmente selon de statut de l’emprunteur (TRUE ou FLASE). Les taux d’intérêt sont plus faibles, en moyenne, pour les emprunteurs dont l’auto-déclaration du revenu n’a pas été vérifiée.

ggplot_box(data2, data2$verification_status, data2$int_rate, data2$default, "Interest Rate by Verification Status", "Verification status", "Interest rate \n")

Variable : “installment”

Il s’agit de la mensualité due par l’emprunteur si le prêt est accordé.

Observation : le montant des versements varie largement, avec une distribution assez étalée vers la droite ce qui montre la présence de très grosses mensualités (mensualité maximale : 1354.66$). Avec les boxplot, on ne voit pas de différence significative entre les mensualités d’un emprunteur qui a totalement remboursé son crédit par rapport à celui en défaut sauf pour les mensualités minimales et maximales. Dans l’ensemble, les mensualités moyennes des classes sont relativement proches. Forcément, c’est les emprunteurs qui possèdent les plus faibles mensualités en moyenne. Pour la médiane, on observe la même chose avec une distinction inter-classe légèrement plus forte. En revanche, c’est un emprunteur qui a effectivement remboursé son emprunt qui détient la mensualité la plus élevée de notre échantillon. Une fois encore, plusieurs outliers sont identifiés au sein de cette caractéristique. Nous verrons lors du pre-processsing comment les traiter.

fig1 <- plot_ly(data, x = ~data$installment, type = "histogram", marker = list(color = "#BCA9F5"))
fig2 <- plot_ly(data, y = ~data$installment, x = ~data$loan_status, type = "box", marker = list(color = "#82caaf"))

fig <- subplot(fig1, fig2, nrows = 2) 
fig <- fig %>% layout(title = "Installment variable",
         plot_bgcolor="#FFFFFF", 
         xaxis = list(
           title = "Installment",
           zerolinecolor = "#ffff", 
           zerolinewidth = 2, 
           gridcolor = "ffff"), 
         yaxis = list(
           title = "Count",
           zerolinecolor = "#ffff", 
           zerolinewidth = 2, 
           gridcolor = "ffff"), 
         showlegend = FALSE,
         showlegend2 = FALSE)

fig
stat_desc <- descriptive_statistic1(data, data$installment)
stat_desc
data %>%
  group_by(loan_status) %>%
  summarise(Minimum = round(min(installment), digits = 4),
            Maximum = round(max(installment), digits = 4),
            Moyenne = round(mean(installment), digits = 4),
            Mediane = round(median(installment), digits = 4),
            Variance = round(var(installment), digits = 4),
            Ecart_type = round(sd(installment), digits = 4),
            Kurtosis = round(kurtosis(installment), digits = 4),
            Skewness = round(skewness(installment), digits = 4))
`summarise()` ungrouping output (override with `.groups` argument)

Quid de la mensualité selon le montant emprunté et la statut de l’emprunteur ?

ggplot_box(data2, data2$installment, data2$loan_amnt, data2$default, "Loan amount by Installment", "Installment", "Loan amount \n")

Variables catégorielles

Nous allons faire un focus sur les distributions des différentes modalités pour 6 variables qualitatives. - “home_ownership” - “term” - “verification_status” - “purpose” - “grade” - “pub_rec_bankruptcies”

Observations : - La grande majorité des personnes demandant des prêts sont des personnes étant en location ou en hypothèque => emprunteurs en situation précaire. - La très grande majorité des prêts financés sur la plateforme sont d’une durée de 36 mois. Environ 75% du total des prêts sont accordés pour une durée de 3 ans, tandis que seulement 25% des prêts sont accordés pour une durée de 5 ans. - La plupart des prêts approuvés sont de qualité supérieure. Néanmoins, une part non négligeable de prêts sont accordés sans vérification. - Très peu de prêts sont accordés aux personnes ayant déjà fait faillite, et donc ayant une mauvaise note. - La très très grande majorité des prêts sont accordés pour des personnes n’ayant déclarées aucune faillite publique. - Le motif de consolidation de la dette est de loin le principal motif de demande de prêt => emprunteurs en situation précaire.

fig1 <- plot_histogram(data, data$home_ownership, "Home ownership distribution", "Home ownership", "Count")
fig2 <- plot_histogram(data, data$term, "Loan term distribution", "Term", "Count")
fig3 <- plot_histogram(data, data$verification_status, "Verified status proportion", "Verified status", "Count")
fig4 <- plot_histogram(data, data$grade, "Credit grade repartition", "Grade", "Count")
fig5 <- plot_histogram(data, data$pub_rec_bankruptcies, "Public bankruptcy distribution", "Public bankruptcy", "Count")
fig6 <- plot_histogram(data, data$purpose, "Loan purpose repartition", "Purpose", "Count")

fig <- subplot(fig1, fig2, fig3, fig4, fig5, fig6, nrows = 3, titleY = TRUE, titleX = TRUE, margin = 0.1)
fig <- fig %>% layout(title = "Cetgorical variable",
         plot_bgcolor="#FFFFFF", 
         xaxis = list(
           zerolinecolor = "#ffff", 
           zerolinewidth = 2, 
           gridcolor = "ffff"), 
         yaxis = list(
           zerolinecolor = "#ffff", 
           zerolinewidth = 2, 
           gridcolor = "ffff"), 
         showlegend = FALSE,
         showlegend2 = FALSE)

fig

Variable : “sub_grade”

La plateforme Lending Club attribue un grade ainsi qu’un sous-grade à chaque prêt.

Observation : - Les prêts de catégorie A et B sont les plus sûrs. - Les prêts de catégorie D, E, F et G sont moins sûrs. - Les prêts impayés sont en majorité de catégorie C et D. - Les prêts à partir de la catégorie E, F et G sont risqués et moins nombreux. - Ainsi, on peut dire que le système de classement de Lending Club fonctionne.

fig <- plot_histogram(data, data$sub_grade, "Credit sub-grade distribution", "Sub-grade", "Count")
fig
fig <- plot_box(data, data$grade, data$loan_status, "Credit grade distribution", "Grade", "Count")
fig
fig <- plot_box(data, data$sub_grade, data$loan_status, "Credit sub-grade distribution", "Sub-grade", "Count")
fig

Variable : annual income

Le revenu annuel auto-déclaré par l’emprunteur lors de son inscription.

Observation : lorsque l’on regarde la répartition des revenus annuels, on voit que la distribution comporte de nombreuses valeurs extrêmes car la distribution est leptokurtic (kurtosis égale à 970.3) et très très étalée vers la droite (la preuve avec une skewness de 18.7). Nous observons un écart de 10 000$ entre la médiane et la moyenne. Les moyennes et médianes des différentes classes sont relativement “proches”. En revanche nous pouvons voir que pour les emprunteurs en période de grâce, leur moyenne et médiane sont anormalement élevées en raison du faible nombre d’observations de cette classe dans notre échantillon.

fig <- plot_subplot(data, data$annual_inc, data$loan_status, "Annual income variable", "Annual income", "Count")
fig
stat_desc <- descriptive_statistic1(data, data$annual_inc)
stat_desc
data %>%
  group_by(loan_status) %>%
  summarise(Minimum = round(min(annual_inc), digits = 4),
            Maximum = round(max(annual_inc), digits = 4),
            Moyenne = round(mean(annual_inc), digits = 4),
            Mediane = round(median(annual_inc), digits = 4),
            Variance = round(var(annual_inc), digits = 4),
            Ecart_type = round(sd(annual_inc), digits = 4),
            Kurtosis = round(kurtosis(annual_inc), digits = 4),
            Skewness = round(skewness(annual_inc), digits = 4))
`summarise()` ungrouping output (override with `.groups` argument)

Quid du revenu annuel auto-déclaré selon le grade de crédit Lending Club de l’emprunteur et le statut de l’emprunteur ? Nous remarquons que les grades les mieux notés sont ceux qui affichent les plus forts revenus annuels. Les revenus décroissent avec les grades.

ggplot_box(data2, data2$grade, data2$annual_inc, data2$default, "Annual income by Grade", "Grade", "Annual income \n")

Quid du revenu annuel auto-déclaré selon le grade de crédit Lending Club de l’emprunteur et l’échéance de crédit ? Nous avons l’impression que les emprunteurs avec les plus hauts revenus et les meilleurs grades empruntent à court-terme tandis que ceux avec les plus faibles revenus annuels et les plus mauvais grades empruntent davantage à long-terme.

data2 %>%
  ggplot(aes(grade, annual_inc)) +
  geom_boxplot(fill = "white", colour = "darkblue", 
               outlier.colour = "red", outlier.shape = 1) +
  give_count +
  give_mean +
  scale_y_continuous(labels = comma) +
  labs(title="Annual income by Grade", x = "Grade", y = "Annual income \n") +
  facet_wrap(~ term)

Variable : addr state

L’état fourni par l’emprunteur dans la demande de prêt.

Observation : les prêts les plus importants proviennent de l’Etat de la Californie, suivi par l’Etat de New-York, de la Floride et du Texas. Il faut s’y attendre car ce sont également les trois États américains les plus peuplés. Les États ayant un taux de défaillance plus élevé ont un nombre de prêts très faible. Le pourcentage n’est donc PAS significatif et doit être ignoré. Globalement, cette variable n’affecte pas la propension au défaut.

fig <- plot_histogram(data, data$addr_state, "Borrower's state", "US state", "Count")
fig

Est-ce que certains états des Etats-Unis affichent des taux de défaut nettement supérieurs à d’autres états ? - Oklahoma : 31.35% - Nebraska : 30.43% Les états de l’Oklahoma et du Nebraska sont ceux affichant un taux de défaut supérieur à 30%.

  • Oregon : 13.25%
  • Dakota du Sud : 12.50% À l’inverse, les états de l’Oregon et du Dakota du Sud sont ceux affichant les plus faibles taux de défaut.

Les états peuvent donc avoir un pouvoir informatif dans la santé financière des emprunteurs. Forcément dans les états où le taux d’emplois, le taux d’urbanisation est fort, le taux de défaut est plus faible.

default_rate_state <- 
  data2 %>%
  select(default, addr_state) %>%
  group_by(addr_state) %>%
  summarise(default_rate = sum(default, na.rm = TRUE) / n())
`summarise()` ungrouping output (override with `.groups` argument)
default_rate_state

Variable : “delinq_2yrs”

Le nombre d’incidences d’impayés de plus de 30 jours dans le dossier de crédit de l’emprunteur au cours des deux dernières années.

Observation : les risques de défaillance sont plus élevés si cette variable est supérieure à 1. Heureusement sa médiane est égale à 0. Nous pouvons voir qu’il n’y a pas une très grande différence entre les classes. Par exemple, la moyenne des emprunteurs en défaut est de 0.36 nombres d’incident sur les 2 dernières années contre 0.33 nombres d’incident pour les emprunteurs ayant totalement remboursés leur emprunt.

fig <- plot_histogram(data, data$delinq_2yrs, "2 years delinq distribution", "2 years delinq", "Count")
fig
stat_desc <- descriptive_statistic1(data, data$delinq_2yrs)
stat_desc
data %>%
  group_by(loan_status) %>%
  summarise(Minimum = round(min(delinq_2yrs), digits = 4),
            Maximum = round(max(delinq_2yrs), digits = 4),
            Moyenne = round(mean(delinq_2yrs), digits = 4),
            Mediane = round(median(delinq_2yrs), digits = 4),
            Variance = round(var(delinq_2yrs), digits = 4),
            Ecart_type = round(sd(delinq_2yrs), digits = 4),
            Kurtosis = round(kurtosis(delinq_2yrs), digits = 4),
            Skewness = round(skewness(delinq_2yrs), digits = 4))
`summarise()` ungrouping output (override with `.groups` argument)

Quid du nombre de délinquance sur les 2 dernières années selon le grade de crédit Lending Club de l’emprunteur et le statut de l’emprunteur ? Avec l’aide des outliers, nous pouvons noter que le nombre de délinquance sur les 2 dernières années a tendance à augmenter pour les emprunteurs en défaut de paiement et d’autant plus pour les grades de crédit les plus mauvais pour les clients en défaut.

ggplot_box(data2, data2$grade, data2$delinq_2yrs, data2$default, "Number of 2y delinq by Grade", "Grade", "Number of 2y delinq \n")

Quid du nombre de délinquance sur les 2 dernières années selon le grade de crédit Lending Club de l’emprunteur et l’échéance de crédit ? Avec l’aide des outliers, nous pouvons noter que le nombre de délinquance sur les 2 dernières années a tendance à augmenter pour les emprunts en défaut de paiement et d’autant plus pour les grades de crédit les plus mauvais pour les emprunts de long-terme.

data2 %>%
  ggplot(aes(grade, delinq_2yrs)) +
  geom_boxplot(fill = "white", colour = "darkblue", 
               outlier.colour = "red", outlier.shape = 1) +
  give_count +
  give_mean +
  scale_y_continuous(labels = comma) +
  labs(title="Number of 2y delinq by Grade", x = "Grade", y = "Number of 2y delinq \n")+
  facet_wrap(~ term)

Variable : “inq_last_6mths”

Nombre de demandes de renseignements au cours des 6 derniers mois (à l’exclusion des demandes de renseignements sur les véhicules et les prêts hypothécaires).

Observation : les emprunteurs qui possèdent plus de deux demandes de renseignements ont un taux de défaillance commençant à être significativement plus élevé.

fig <- plot_histogram(data, data$inq_last_6mths, "Request for information distribution", "Request for information", "Count")
fig
stat_desc <- descriptive_statistic1(data, data$inq_last_6mths)
stat_desc
data %>%
  group_by(loan_status) %>%
  summarise(Minimum = round(min(inq_last_6mths), digits = 4),
            Maximum = round(max(inq_last_6mths), digits = 4),
            Moyenne = round(mean(inq_last_6mths), digits = 4),
            Mediane = round(median(inq_last_6mths), digits = 4),
            Variance = round(var(inq_last_6mths), digits = 4),
            Ecart_type = round(sd(inq_last_6mths), digits = 4),
            Kurtosis = round(kurtosis(inq_last_6mths), digits = 4),
            Skewness = round(skewness(inq_last_6mths), digits = 4))
`summarise()` ungrouping output (override with `.groups` argument)

Quid du nombre de demandes de renseignements au cours des 6 derniers mois selon le grade de crédit Lending Club de l’emprunteur et le statut de l’emprunteur ? En moyenne, plus le grade de crédit se détériore et plus le nombre de demandes de renseignements au cours des 6 derniers mois tend à augmenter pour les deux classes.

ggplot_box(data2, data2$grade, data2$inq_last_6mths, data2$default, "Number of 6m inq by Grade", "Grade", "Number of 6m inq \n")

Quid du nombre de demandes de renseignements au cours des 6 derniers mois selon le grade de crédit Lending Club de l’emprunteur et l’échéance de crédit ? En moyenne, plus le grade de crédit se détériore et plus le nombre de demandes de renseignements au cours des 6 derniers mois tend à augmenter pour les deux horizons d’emprunt. Nous pouvons tout de même remarquer que la moyenne du nombre de demandes de renseignements au cours des 6 derniers mois a tendance à augmenter plus lentement pour les emprunts sur 60 mois que sur 36. En général, c’est à partir du grade D que la moyenne augmente assez fortement par rapport au grade précédent.

data2 %>%
  ggplot(aes(grade, inq_last_6mths)) +
  geom_boxplot(fill = "white", colour = "darkblue", 
               outlier.colour = "red", outlier.shape = 1) +
  give_count +
  give_mean +
  scale_y_continuous(labels = comma) +
  labs(title="Number of 6m inq by Grade", x = "Grade", y = "Number of 6m inq \n")+
  facet_wrap(~ term)

Variable : “open_acc”

Il s’agit du nombre de lignes de crédit ouvertes dans le dossier de crédit de l’emprunteur.

Observation : il n’y a pas de différence significative entre les lignes de crédit des prêts en défaut, ou autre, et celles des prêts entièrement remboursés (moyenne et médiane très proches). Notons qu’il y a de nombreux outliers à traiter.

fig <- plot_subplot(data, data$open_acc, data$loan_status, "Open credit line variable", "Open credit line", "Count")
fig
stat_desc <- descriptive_statistic1(data, data$open_acc)
stat_desc
data %>%
  group_by(loan_status) %>%
  summarise(Minimum = round(min(open_acc), digits = 4),
            Maximum = round(max(open_acc), digits = 4),
            Moyenne = round(mean(open_acc), digits = 4),
            Mediane = round(median(open_acc), digits = 4),
            Variance = round(var(open_acc), digits = 4),
            Ecart_type = round(sd(open_acc), digits = 4),
            Kurtosis = round(kurtosis(open_acc), digits = 4),
            Skewness = round(skewness(open_acc), digits = 4))
`summarise()` ungrouping output (override with `.groups` argument)

Variable : “total_acc”

Il s’agit du nombre total de lignes de crédit figurant actuellement dans le dossier de crédit de l’emprunteur.

Observation : il n’y a pas de différence significative entre les lignes de crédit des prêts en défaut, ou autre, et celles des prêts entièrement remboursés (moyenne et médiane très proches). Notons qu’il y a de nombreux outliers à traiter.

fig <- plot_subplot(data, data$total_acc, data$loan_status, "Totla credit line variable", "Totla credit line", "Count")
fig
stat_desc <- descriptive_statistic1(data, data$total_acc)
stat_desc
data %>%
  group_by(loan_status) %>%
  summarise(Minimum = round(min(total_acc), digits = 4),
            Maximum = round(max(total_acc), digits = 4),
            Moyenne = round(mean(total_acc), digits = 4),
            Mediane = round(median(total_acc), digits = 4),
            Variance = round(var(total_acc), digits = 4),
            Ecart_type = round(sd(total_acc), digits = 4),
            Kurtosis = round(kurtosis(total_acc), digits = 4),
            Skewness = round(skewness(total_acc), digits = 4))
`summarise()` ungrouping output (override with `.groups` argument)

Quid du nombre de lignes de crédit ouvertes selon le grade de crédit Lending Club de l’emprunteur et le statut de l’emprunteur ? Ici, nous aurions pu nous attendre au fait que les emprunteurs en défaut aient davantage de lignes de crédit ouvertes. En réalité cela n’est pas vraiment vérifié. On remarque même qu’il n’y a pas vraiment de différence entre les grades de crédit. Par exemple pour les clients sains, le grade A possède, en moyenne, environ 25 lignes de crédit ouvertes au total sur toutes leur vie tandis que le grade G un peu moins. Attention cette interprétation est à prendre avec de grosses pincettes étant donné que nous n’avons pas autant d’observations dans ces deux grades de crédit et qu’ils ne sont donc pas très comparables. La légère décroissance du nombre de lignes de crédit ouvertes par emprunteur s’explique par la décroissance du nombre d’observations dans les grades les plus mauvais.

ggplot_box(data2, data2$grade, data2$total_acc, data2$default, "Number of opened credit line by Grade", "Grade", "Number of opened credit line \n")

Variable : “last_pymnt_amnt”

Dernier montant total du paiement reçu.

Observation : le montant du dernier versement reçu est nettement inférieur pour les prêts impayés par rapport aux prêts entièrement remboursés. Il y a un écart assez fort entre les prêts totalement remboursés et les autres : la moyenne et médiane sont relativement bien plus élevées (moyenne fully paid 5837.84 contre moyenne charged off 455.21). De nombreux outliers pour la modalité des prêts totalement remboursés.

fig <- plot_subplot(data, data$last_pymnt_amnt, data$loan_status, "Total paid amount variable", "Total paid amount", "Count")
fig
stat_desc <- descriptive_statistic1(data, data$last_pymnt_amnt)
stat_desc
data %>%
  group_by(loan_status) %>%
  summarise(Minimum = round(min(last_pymnt_amnt), digits = 4),
            Maximum = round(max(last_pymnt_amnt), digits = 4),
            Moyenne = round(mean(last_pymnt_amnt), digits = 4),
            Mediane = round(median(last_pymnt_amnt), digits = 4),
            Variance = round(var(last_pymnt_amnt), digits = 4),
            Ecart_type = round(sd(last_pymnt_amnt), digits = 4),
            Kurtosis = round(kurtosis(last_pymnt_amnt), digits = 4),
            Skewness = round(skewness(last_pymnt_amnt), digits = 4))
`summarise()` ungrouping output (override with `.groups` argument)

Variable : FICO score

  • fico_range_high
  • fico_range_low
  • last_fico_range_high
  • last_fico_range_low

Les scores de crédit FICO sont une méthode de quantification et d’évaluation de la solvabilité d’une personne. Les scores vont de 300 à 850, ceux compris entre 670 et 739 étant considérés comme de “bons” antécédents de crédit, de 740 à 799 comme de “très bons” antécédents de crédit et les scores supérieurs à 800 sont considérés comme d’ “excellent” antécédents de crédit.

Observation : on remarque bien évidement que plus le score de FICO est élevé, en moyenne, plus le risque de défaut est faible.

Dans un premier temps on regarde la limite inférieure de la fourchette à laquelle appartient le FICO de l’emprunteur au moment de l’octroi du prêt. On voit que la distribution des scores est très très étalée vers la droite. Il y a donc une skewness fortement positive et la présence de valeurs extrêmement positives.

Dans un second temps, on regarde la fourchette de limites à laquelle appartient le dernier FICO tiré de l’emprunteur. On retrouve la même conclusion : plus le score de FICO est élevé, plus le risque de défaut est faible en moyenne. Concernant la distribution des scores, elle semble relativement centrée sur de “bons” score de FICO.

fig <- plot_subplot(data, data$fico_range_high, data$loan_status, "High FICO variable", "High FICO", "Count")
fig
fig <- plot_subplot(data, data$fico_range_low, data$loan_status, "Low FICO variable", "Low FICO", "Count")
fig
fig <- plot_subplot(data, data$last_fico_range_high, data$loan_status, "Last high FICO variable", "Last high FICO", "Count")
fig
fig <- plot_subplot(data, data$last_fico_range_low, data$loan_status, "Last low FICO variable", "Last low FICO", "Count")
fig

Variables :

  • last_fico_range_high
  • last_fico_range_low
  • grade
  • sub_grade

Observation : comme nous l’attendions, meilleure est la note de crédit accordée à l’emprunteur (grade et sub-grade), meilleur est son score de FICO. Ceci est logique, un bon score de FICO correspond naturellement à une bonne notation de crédit pour l’emprunteur.

fig1 <- plot_box(data, data$last_fico_range_high, data$grade, "Last high FICO variable", "Last high FICO", "Count")
fig2 <- plot_box(data, data$last_fico_range_low, data$grade, "Last low FICO variable", "Last low FICO", "Count")

fig <- subplot(fig1, fig2, nrows = 2, titleY = TRUE, titleX = TRUE, margin = 0.1)
fig <- fig %>% layout(title = "Last FICO by grade",
         plot_bgcolor="#FFFFFF", 
         xaxis = list(
           zerolinecolor = "#ffff", 
           zerolinewidth = 2, 
           gridcolor = "ffff"), 
         yaxis = list(
           zerolinecolor = "#ffff", 
           zerolinewidth = 2, 
           gridcolor = "ffff"), 
         showlegend = FALSE,
         showlegend2 = FALSE)

fig
fig1 <- plot_box(data, data$last_fico_range_high, data$sub_grade, "Last high FICO variable", "Last high FICO", "Count")
fig2 <- plot_box(data, data$last_fico_range_low, data$sub_grade, "Last low FICO variable", "Last low FICO", "Count")

fig <- subplot(fig1, fig2, nrows = 2, titleY = TRUE, titleX = TRUE, margin = 0.1)
fig <- fig %>% layout(title = "Last FICO by grade",
         plot_bgcolor="#FFFFFF", 
         xaxis = list(
           zerolinecolor = "#ffff", 
           zerolinewidth = 2, 
           gridcolor = "ffff"), 
         yaxis = list(
           zerolinecolor = "#ffff", 
           zerolinewidth = 2, 
           gridcolor = "ffff"), 
         showlegend = FALSE,
         showlegend2 = FALSE)

fig

Synthèse de l’EDA

Caractéristiques potentiellement intéressantes

data3 <- subset(data2, select = c("default", "loan_amnt", "funded_amnt", "int_rate", "installment", "annual_inc", "delinq_2yrs", "inq_last_6mths", "mths_since_last_delinq", "open_acc", "total_acc", "fico_range_high", "term", "grade", "home_ownership", "verification_status", "purpose", "addr_state", "application_type", "pub_rec_bankruptcies"))

str(data3)
'data.frame':   17839 obs. of  20 variables:
 $ default               : logi  FALSE FALSE FALSE FALSE FALSE FALSE ...
 $ loan_amnt             : num  3600 24700 20000 10400 11950 ...
 $ funded_amnt           : num  3600 24700 20000 10400 11950 ...
 $ int_rate              : num  14 12 10.8 22.4 13.4 ...
 $ installment           : num  123 820 433 290 405 ...
 $ annual_inc            : num  55000 65000 63000 104433 34000 ...
 $ delinq_2yrs           : num  0 1 0 1 0 0 1 0 0 0 ...
 $ inq_last_6mths        : num  1 4 0 3 0 0 0 1 0 0 ...
 $ mths_since_last_delinq: num  30 6 NA 12 NA 49 3 NA 75 NA ...
 $ open_acc              : num  7 22 6 12 5 12 8 14 8 17 ...
 $ total_acc             : num  13 38 18 35 6 27 15 23 18 24 ...
 $ fico_range_high       : num  679 719 699 699 694 684 709 689 704 704 ...
 $ term                  : chr  " 36 months" " 36 months" " 60 months" " 60 months" ...
 $ grade                 : chr  "C" "C" "B" "F" ...
 $ home_ownership        : chr  "MORTGAGE" "MORTGAGE" "MORTGAGE" "MORTGAGE" ...
 $ verification_status   : chr  "Not Verified" "Not Verified" "Not Verified" "Source Verified" ...
 $ purpose               : chr  "debt_consolidation" "small_business" "home_improvement" "major_purchase" ...
 $ addr_state            : chr  "PA" "SD" "IL" "PA" ...
 $ application_type      : chr  "Individual" "Individual" "Joint App" "Individual" ...
 $ pub_rec_bankruptcies  : num  0 0 0 0 0 0 0 1 0 0 ...

Réaliser des graphiques de densité avec KDE nous permet de vraiment visualiser la distinction inter-classe. Pour les variables continues, on remarque bien que certaines caractéristiques apportent une bonne information. Quelques exemples : - Le score de FICO, c’est assez léger mais nous voyons bien que pour les emprunteurs n’étant pas en défaut les scores ont tendance à être supérieur. On voit même que pour la classe TRUE il y a un pic d’observation au début de la distribution ce qui montre qu’une grosse par des emprunteurs en défaut possèdent de faibles scores de FICO ; - La variable du taux d’intérêt permet aussi de bien montrer le clivage entre les deux classes avec des taux qui sont plus élevés pour les emprunteurs en défaut ; - Les emprunteurs en défaillance ont également tendance à emprunter de plus gros montants, la majorité des emprunteurs sains empruntent quasiment le montant moyen octroyé (10 / 12k $) ; - Le nombre total de lignes de crédit ouvertes n’est pas vraiment discriminant afin de dissocier les deux classes comme nous l’avions notifié précédemment lors de l’analyse de la caractéristique ; - Notons également que les caractéristiques “loan_amnt” et “funded_amnt” ont une distribution de valeurs très similaires ce qui indiquerait une forte redondance d’information donc une mutli-colinéarité imparfaite à supprimer pour ne pas biaiser notre future modélisation.

Plot des caractéristiques quantitatives intéressantes

num_vars <- data3 %>% sapply(is.numeric) %>% which() %>% names()

data3 %>%
  select_(.dots = num_vars) %>%
  gather(measure, value) %>%
  mutate(default = factor(rep(x = data3$default, 
                              length.out = length(num_vars) * dim(data3)[1]), 
                          levels = c("TRUE", "FALSE"))) %>%
  ggplot(data = ., aes(x = value, fill = default, 
                       color = default, order = -default)) +
  geom_density(alpha = 0.3, size = 0.5) +
  scale_fill_brewer(palette = "Set1") +
  scale_color_brewer(palette = "Set1") +
  facet_wrap( ~ measure, scales = "free", ncol = 3)
`select_()` was deprecated in dplyr 0.7.0.
Please use `select()` instead.

Concernant les variables catégorielles : - Les demandes de prêt sont généralement pour un emprunt seul et pas conjoint ; - La proportion d’emprunteur en défaut est élevée dans les grades B, C et D ; - La proportion d’emprunteur en défaut est élevée pour les emprunteurs en hypothèque ou en location immobilière ; - De nombreux emprunteurs défaillants ont obtenu un prêt pour le motif de la consolidation de dette très principalement ; - L’échéance de l’emprunt et la vérification du statut ne permettent pas de confondre les deux types d’emprunteur car les modalités des variables affichent les mêmes proportions.

Plot des caractéristiques qualitatives intéressantes

char_vars <- data3 %>% sapply(is.character) %>% which() %>% names()

data3 %>%
  select_(.dots = char_vars) %>%
  gather(measure, value) %>%
  mutate(default = factor(rep(x = data3$default, 
                              length.out = length(char_vars) * dim(data3)[1]), 
                          levels = c("TRUE", "FALSE"))) %>%
  ggplot(data = ., aes(x = value, fill = default, 
                       color = default, order = -default)) +
  geom_histogram(stat = "count", alpha = 0.5) +
  scale_fill_brewer(palette = "Set1") +
  scale_color_brewer(palette = "Set1") +
  facet_wrap( ~ measure, scales = "free", ncol = 3)
Ignoring unknown parameters: binwidth, bins, pad


LS0tCnRpdGxlOiAiRURBIG5vdGVib29rIgpvdXRwdXQ6CiAgd29yZF9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKLS0tCgpCQVJCRVkgQ2hhcmxvdHRlIGV0IFBSVVRLSSBMdWNhcwoKTTIgTW9TRUYgLSBEYXRhIE1pbmluZwoKCiMgRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcyAoRURBKQoKCiMjIyMgTGlicmFpcmllcwpgYGB7cn0KIyBEw6lmaW5pciBsZXMgbGlicmFpcmllcyBxdWUgbCdvbiB2YSB1dGlsaXNlcgpsaWJyYXJpZXNfdXNlZCA8LSAKICBjKCJkcGx5ciIsICJmdW5Nb2RlbGluZyIsICJnZ3Bsb3QyIiwgIlBlcmZvcm1hbmNlQW5hbHl0aWNzIiwgInBsb3RseSIsICJzY2FsZXMiLCAidGlkeXIiLCAidGlueXRleCIpCgojIFbDqXJpZmljYXRpb24gZGVzIGxpYnJhaXJpZXMgaW5zdGFsbMOpZXMKbGlicmFyaWVzX21pc3NpbmcgPC0gCiAgbGlicmFyaWVzX3VzZWRbIShsaWJyYXJpZXNfdXNlZCAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpWywiUGFja2FnZSJdKV0KCiMgSW5zdGFsbGVyIGxlcyBsaWJyYWlyaWVzIG1hbnF1YW50ZXMKaWYobGVuZ3RoKGxpYnJhcmllc19taXNzaW5nKSkgaW5zdGFsbC5wYWNrYWdlcyhsaWJyYXJpZXNfbWlzc2luZykKYGBgCgoKIyMjIyBJbXBvcnQgZGVzIGxpYnJhaXJpZXMKYGBge3IgfQoKbGlicmFyeShkcGx5cikKbGlicmFyeShmdW5Nb2RlbGluZykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KFBlcmZvcm1hbmNlQW5hbHl0aWNzKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkodGlueXRleCkKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMjIEltcG9ydCBkZXMgZG9ubsOpZXMKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gIi9Vc2Vycy9jaGFybG90dGUvRGVza3RvcC9wcm9qZXRfZGF0YW1pbmluZy9kYXRhLyIpCmBgYAoKCmBgYHtyfQpkYXRhIDwtIHJlYWQuY3N2KCJkYXRhX2xlbmRpbmdfY2x1Yi5jc3YiKQpgYGAKCgpMYSBiYXNlIGRlIGRvbm7DqWVzIGNvbXBvcnRlIDE1MSB2YXJpYWJsZXMuIE5vdXMgbidhbGxvbnMgcGFzIHRvdXRlcyBsZXMgcmVnYXJkZXIgdW5lIHBhciB1bmUuIGVuIHJldmFuY2hlLCBub3VzIGFsbG9ucyBjaWJsZXIgbGVzIHBsdXMgaW50w6lyZXNzYW50ZXMgZXQgY2VsbGVzIHF1aSBzb250IHN1c2NlcHRpYmxlcyBkJ2V4cGxpcXVlciBhdSBtaWV1eCBwb3VycXVvaSB1biBpbnZlc3Rpc3NldXIgcG91cnJhaXQgZmFpcmUgdW4gZMOpZmF1dCBkZSBwYWllbWVudC4KRW4gb3V0cmUsIGwnZXhwbG9yYXRpb24gZ3JhcGhpcXVlIGRlIGxhIGJhc2UgZGUgZG9ubsOpZXMgdmlzZSBhdXNzaSDDoCBjb21wcmVuZHJlIG5vcyBkb25uw6llcywgw6Agc2F2b2lyIGNvbW1lbnQgbGVzIG5ldHRveWVyIG1haXMgYXVzc2kgZXQgc3VydG91dCDDoCDDqW1ldHRyZSBub3MgcHJlbWnDqHJlcyBoeXBvdGjDqHNlcyBxdWFudCDDoCBsYSBwcm9ibMOpbWF0aXF1ZSBwb3PDqWUgcGFyIGxlIHByb2pldCBkJ2ludmVzdGlzc2VtZW50LgoKIyMjIyBEZXNjcmlwdGlvbiBkZXMgZG9ubsOpZXMKYGBge3J9CnN0cnVjdHVyZV9kYXRhIDwtIHN0cihkYXRhKQpzdHJ1Y3R1cmVfZGF0YQpgYGAKCgpQcsOpY8OpZGVtbWVudCwgbm91cyBhdm9ucyBwcmlzIHVuZSB2dWUgdHLDqHMgZ2xvYmFsZSBkZSBsYSBiYXNlIGRlIGRvbm7DqWVzIGFmaW4gZCdlbiBhdm9pciB1biBwcmVtaWVyIGFwZXLDp3UuIE9uIHZhIGRvbmMgcG91dm9pciByZWdhcmRlciBhdmVjIHBsdXMgZGUgZMOpdGFpbHMgbGVzIGRpZmbDqXJlbnRlcyB2YXJpYWJsZXMgcXVpIGxhIGNvbXBvc2VudCBldCBkZSBjb21tZW5jZXIgw6AgbCdleHBsb3Jlci4KTGEgY2VsbHVsZSwgY2ktZGVzc291cywgbm91cyByZW52b2llIGRlIG5vbWJyZXVzZXMgaW5mb3JtYXRpb25zIDogCi0gTm9tYnJlIGRlIHZhbGV1cnMgbnVsbGVzIHBhciB2YXJpYWJsZSBhaW5zaSBxdWUgbGVzIHByb3BvcnRpb25zIGVuIHBvdXJjZW50YWdlIDsKLSBOb21icmUgZGUgdmFsZXVycyBtYW5xdWFudGVzIHBhciB2YXJpYWJsZSBhaW5zaSBxdWUgbGVzIHByb3BvcnRpb25zIGVuIHBvdXJjZW50YWdlIDsKLSBOb21icmUgZGUgdmFsZXVycyBpbmZpbmllcyBwYXIgdmFyaWFibGUgYWluc2kgcXVlIGxlcyBwcm9wb3J0aW9ucyBlbiBwb3VyY2VudGFnZSA7Ci0gTGUgdHlwZSBkZSBjaGFxdWUgdmFyaWFibGUgOwotIExlIG5vbWJyZSBkZSB2YWxldXJzIHVuaXF1ZXMgcXVlIGNvbXBvcnRlIGNoYXF1ZSB2YXJpYWJsZS4KCkxhIHByZW1pw6hyZSBjaG9zZSBxdWUgbCdvbiB2b2l0IGMnZXN0IHF1J2lsIG5lIHNlbWJsZSBwYXMgYXZvaXIgZGUgZG91YmxvbnMgZGFucyBsYSBiYXNlLiBQb3VycXVvaSA/IGxhIHZhcmlhYmxlICJJZCIgcXVpIHJlcHLDqXNlbnRlIGwnaWRlbnRpZmlhbnQgdW5pcXVlIExlbmRpbmcgQ2x1YiBkZSBjaGFxdWUgZW1wcnVudGV1ciBkZSBsYSBwbGF0ZWZvcm1lIHBvc3PDqGRlIDIwIDAwMCB2YWxldXJzIHVuaXF1ZXMsIHNvaXQgdW5lIHBhciBjbGllbnQuCk9uIHJlbWFycXVlLCDDqWdhbGVtZW50LCBxdWUgZGVzIHZhcmlhYmxlcyBwb3Nzw6hkZW50IDEwMCUgZGUgdmFsZXVycyBtYW5xdWFudGVzICjDoCBsJ2luc3RhciBkZSBsYSBjYXJhY3TDqXJpc3RpcXVlICJyZXZvbF9iYWxfam9pbnQiKSBvdSBkZXMgcHJvcG9ydGlvbnMgdHLDqHMgcG9jaGVzIGRlIDEwMCUgKGxhIHZhcmlhYmxlICJhbm51YWxfaW5jX2pvaW50IiBhdmVjIDk5LjI1JSBkZSB2YWxldXJzIG1hbnF1YW50ZXMpLiDDgCBsJ2ludmVyc2UgZCdhdXRyZXMgdmFyaWFibGVzIGFmZmljaGVudCwgaGV1cmVzZW1lbnQsIDAlIGRlIHZhbGV1cnMgbWFucXVhbnRlcy4KSWwgeSBhdXJhIGRvbmMgdW4gZ3JvcyB0cmF2YWlsIMOgIHLDqWFsaXNlciBzdXIgbGUgdHJhaXRlbWVudCBldCBsYSBnZXN0aW9uIGRlcyB2YWxldXJzIG1hbnF1YW50ZXMgZGFucyBsYSBiYXNlIGRlIGRvbm7DqWVzLgoKIyMjIyBEZXNjcmlwdGlvbiBkZXMgZG9ubsOpZXMKYGBge3J9CmRldGFpbGxlZF9kYXRhIDwtIGRmX3N0YXR1cyhkYXRhLCBwcmludF9yZXN1bHRzID0gRkFMU0UpCmRldGFpbGxlZF9kYXRhCmBgYAoKCkFmZmljaGVyIGxlIG5vbWJyZSBkZSB2YWxldXJzIHVuaXF1ZXMgYXUgc2VpbiBkZXMgZGlmZsOpcmVudGVzIGNhcmFjdMOpcmlzdGlxdWVzIGVzdCB0csOocyBpbmZvcm1hdGlmIGNhciBjZWxhIG5vdXMgZG9ubmUgdW5lIGluZm9ybWF0aW9uIHN1ciBsYSBxdWFsaXTDqSBpbmZvcm1hdGl2ZSBkZSBsYSBjYXJhY3TDqXJpc3RpcXVlIGV1IMOpZ2FyZCDDoCBsJ8OpdsOobmVtZW50IMOgIG1vZMOpbGlzZXIuIElsbHVzdHJvbnMgbm9zIHByb3Bvcywgc2kgdW5lIGNhcmFjdMOpcmlzdGlxdWUgYWZmaWNoZSB1bmUgcHJvcG9ydGlvbiBkZSAwJSBkZSB2YWxldXJzIHVuaXF1ZXMgY2VsYSBzaWduaWZpZSBxdWUgdG91dGVzIGxlcyBvYnNlcnZhdGlvbnMgb250IGV4YWN0ZW1lbnQgbGEgbcOqbWUgdmFsZXVyIHBvdXIgY2V0dGUgdmFyaWFibGUuIENldHRlIGRlcm5pw6hyZSBuZSBwZXJtZXQgcGFzIGRlIGRpc2NyaW1pbmVyIGxlcyBpbmRpdmlkdXMgcG91ciBsZSBwaMOpbm9tw6huZSDDqXR1ZGnDqSBldCBkZSBmYWN0byBuZSB2w6loaWN1bGUgYXVjdW5lIHZhbGV1ciBkJ2luZm9ybWF0aW9uLiBFbGxlIHBvdXJyYSDDqnRyZSBzdXBwcmltw6llIHBvdXIgbGEgZnV0dXJlIGFuYWx5c2UuCkVuIHJldmFuY2hlIHVuZSB2YXJpYWJsZSB0ZWxsZSBxdWUgbGUgcmV2ZW51IGFubnVlbCBkJ3VuIGVtcHJ1bnRldXIgcXVpIGRpc3Bvc2UgZGUgMTAwJSBkZSB2YWxldXJzIHVuaXF1ZXMgKGkuZS4gc29pdCBkZSAyMCAwMDAgdmFsZXVycyBkaWZmw6lyZW50ZXMpIHNpZ25pZmllIHF1ZSBjaGFxdWUgY2xpZW50IHBvc3PDqGRlIHVuIHJldmVudSBkaWZmw6lyZW50IGNlIHF1aSBwZXV0IHBlcm1ldHRyZSBkZSBkaXNjcmltaW5lciBjZXMgY2xpZW50cyBwYXIgcmFwcG9ydCDDoCBub3RyZSB2YXJpYWJsZSBjaWJsZSDDoCBtb2TDqWxpc2VyLiBDZXR0ZSBjYXJhY3TDqXJpc3RpcXVlIHBldXQgZG9uYyDDqnRyZSBjb25zZXJ2w6llIHBvdXIgbGEgbW9kw6lsaXNhdGlvbiBmdXR1cmUuCklsIHMnYWdpdCBkb25jIGQnaWRlbnRpZmllciBjZXMgdmFyaWFibGVzIHF1aSBzb250IGRlcyBjb25zdGFudGVzIGFmaW4gZGUgbGVzIHN1cHByaW1lciBldCDDqXZpdGVyIHRvdXQgYmlhaXMgZGFucyBub3RyZSBhbmFseXNlLgpFeGVtcGxlIDogIm1lbWJlcl9pZCIsICJpc3N1ZV9kIiwgInBvbGljeV9jb2RlIiwgImhhcmRzaGlwX2xlbmd0aCIsICJkZWZlcnJhbF90ZXJtIiwgIm5leHRfcHltbnRfZCIsIGV0Yy4KCiMjIyMgRGVzY3JpcHRpb24gZGVzIGRvbm7DqWVzCmBgYHtyfQpkZXRhaWxsZWRfZGF0YSA8LQogIGRldGFpbGxlZF9kYXRhICU+JQogIG11dGF0ZSh1bmlxX3JhdGUgPSB1bmlxdWUgLyBucm93KGRhdGEpKQoKZGV0YWlsbGVkX2RhdGEgJT4lCiAgc2VsZWN0KHZhcmlhYmxlLCB1bmlxdWUsIHVuaXFfcmF0ZSkgJT4lCiAgbXV0YXRlKHVuaXF1ZSA9IHVuaXF1ZSwgdW5pcV9yYXRlID0gcGVyY2VudCh1bmlxX3JhdGUpKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIyMgRMOpZmluaXRpb24gZGVzIGZvbmN0aW9ucwpgYGB7cn0KIyBQbG90IGRlcyBzdWJwbG90cyBjb21wb3PDqSBkJ3VuIGhpc3RvZ3JhbSBldCBkJ3VuIGJveHBsb3QKcGxvdF9zdWJwbG90IDwtIGZ1bmN0aW9uKGRhdGEsIGNvbHVtbiwgYnljb2x1bW4sIHRpdGxlLCB4dGl0bGUsIHl0aXRsZSkgewogIAogICAgZmlnMSA8LSBwbG90X2x5KGRhdGEsIHggPSB+Y29sdW1uLCB0eXBlID0gImhpc3RvZ3JhbSIsIG1hcmtlciA9IGxpc3QoY29sb3IgPSAiI0JDQTlGNSIpKQogICAgZmlnMiA8LSBwbG90X2x5KGRhdGEsIHkgPSB+Y29sdW1uLCB4ID0gfmJ5Y29sdW1uLCB0eXBlID0gImJveCIsIG1hcmtlciA9IGxpc3QoY29sb3IgPSAiIzgyY2FhZiIpKQogICAgCiAgICBmaWcgPC0gc3VicGxvdChmaWcxLCBmaWcyLCBucm93cyA9IDIpIAogICAgZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gdGl0bGUsCiAgICAgICAgICAgICBwbG90X2JnY29sb3I9IiNGRkZGRkYiLCAKICAgICAgICAgICAgIHhheGlzID0gbGlzdCgKICAgICAgICAgICAgICAgdGl0bGUgPSB4dGl0bGUsCiAgICAgICAgICAgICAgIHplcm9saW5lY29sb3IgPSAiI2ZmZmYiLCAKICAgICAgICAgICAgICAgemVyb2xpbmV3aWR0aCA9IDIsIAogICAgICAgICAgICAgICBncmlkY29sb3IgPSAiZmZmZiIpLCAKICAgICAgICAgICAgIHlheGlzID0gbGlzdCgKICAgICAgICAgICAgICAgdGl0bGUgPSB5dGl0bGUsCiAgICAgICAgICAgICAgIHplcm9saW5lY29sb3IgPSAiI2ZmZmYiLCAKICAgICAgICAgICAgICAgemVyb2xpbmV3aWR0aCA9IDIsIAogICAgICAgICAgICAgICBncmlkY29sb3IgPSAiZmZmZiIpLCAKICAgICAgICAgICAgIHNob3dsZWdlbmQgPSBGQUxTRSwKICAgICAgICAgICAgIHNob3dsZWdlbmQyID0gRkFMU0UpCiAgICAKICAgIHJldHVybihmaWcpCn0KCiMgUGxvdCB1biBoaXN0b2dyYW0KcGxvdF9oaXN0b2dyYW0gPC0gZnVuY3Rpb24oZGF0YSwgY29sdW1uLCB0aXRsZSwgeHRpdGxlLCB5dGl0bGUpIHsKICAKICAgIGZpZyA8LSBwbG90X2x5KGRhdGEsIHggPSB+Y29sdW1uLCB0eXBlID0gImhpc3RvZ3JhbSIsIG1hcmtlciA9IGxpc3QoY29sb3IgPSAnIzgyY2FhZicpKQogICAgZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gdGl0bGUsCiAgICAgICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSB4dGl0bGUpLAogICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0geXRpdGxlKSkKICAgIAogICAgcmV0dXJuKGZpZykKfQoKIyBQbG90IHVuIGJveHBsb3QKcGxvdF9ib3ggPC0gZnVuY3Rpb24oZGF0YSwgY29sdW1uLCBieWNvbHVtbiwgdGl0bGUsIHh0aXRsZSwgeXRpdGxlKSB7CiAgCiAgICBmaWcgPC0gcGxvdF9seShkYXRhLCB5ID0gfmNvbHVtbiwgeCA9IH5ieWNvbHVtbiwgdHlwZSA9ICJib3giLCBtYXJrZXIgPSBsaXN0KGNvbG9yID0gIiNCQ0E5RjUiKSkKICAgIGZpZyA8LSBmaWcgJT4lIGxheW91dCh0aXRsZSA9IHRpdGxlLAogICAgICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0geHRpdGxlKSwKICAgICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9IHl0aXRsZSkpCiAgICAKICAgIHJldHVybihmaWcpCn0KCiMgQ2FsY3VsZXIgbGVzIHN0YXRpc3RpcXVlcyBkZXNjcmlwdGl2ZXMKZGVzY3JpcHRpdmVfc3RhdGlzdGljMSA8LSBmdW5jdGlvbihkYXRhLCBjb2x1bW4pIHsKICAKICAgIGRhdGEgJT4lCiAgICAgIHN1bW1hcmlzZShNaW5pbXVtID0gcm91bmQobWluKGNvbHVtbiksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICAgICAgTWF4aW11bSA9IHJvdW5kKG1heChjb2x1bW4pLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgICAgIE1veWVubmUgPSByb3VuZChtZWFuKGNvbHVtbiksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICAgICAgTWVkaWFuZSA9IHJvdW5kKG1lZGlhbihjb2x1bW4pLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgICAgIFZhcmlhbmNlID0gcm91bmQodmFyKGNvbHVtbiksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICAgICAgVm9sYXRpbGl0ZSA9IHJvdW5kKHNkKGNvbHVtbiksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICAgICAgS3VydG9zaXMgPSByb3VuZChrdXJ0b3Npcyhjb2x1bW4pLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgICAgIFNrZXduZXNzID0gcm91bmQoc2tld25lc3MoY29sdW1uKSwgZGlnaXRzID0gNCkpCn0KCiMgQ2FsY3VsZXIgbGVzIHN0YXRpc3RpcXVlcyBkZXNjcmlwdGl2ZXMKZGVzY3JpcHRpdmVfc3RhdGlzdGljMiA8LSBmdW5jdGlvbihkYXRhLCBjb2x1bW4pIHsKICAgIGRhdGEgJT4lCiAgICAgIGdyb3VwX2J5KGRhdGEkbG9hbl9zdGF0dXMpICU+JQogICAgICBzdW1tYXJpc2UoTWluaW11bSA9IHJvdW5kKG1pbihjb2x1bW4pLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgICAgIE1heGltdW0gPSByb3VuZChtYXgoY29sdW1uKSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgICAgICBNb3llbm5lID0gcm91bmQobWVhbihjb2x1bW4pLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgICAgIE1lZGlhbmUgPSByb3VuZChtZWRpYW4oY29sdW1uKSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgICAgICBWYXJpYW5jZSA9IHJvdW5kKHZhcihjb2x1bW4pLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgICAgIFZvbGF0aWxpdGUgPSByb3VuZChzZChjb2x1bW4pLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgICAgIEt1cnRvc2lzID0gcm91bmQoa3VydG9zaXMoY29sdW1uKSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgICAgICBTa2V3bmVzcyA9IHJvdW5kKHNrZXduZXNzKGNvbHVtbiksIGRpZ2l0cyA9IDQpKQp9CgojIFBsb3QgdW4gYm94cGxvdApnaXZlX2NvdW50IDwtIAogIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IGZ1bmN0aW9uKHgpIHJldHVybihjKHkgPSBtZWRpYW4oeCkqMS4wNiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGxlbmd0aCh4KSkpLAogICAgICAgICAgICAgICBnZW9tID0gInRleHQiKQoKZ2l2ZV9tZWFuIDwtIAogIHN0YXRfc3VtbWFyeShmdW4ueSA9IG1lYW4sIGNvbG91ciA9ICJkYXJrZ3JlZW4iLCBnZW9tID0gInBvaW50IiwgCiAgICAgICAgICAgICAgIHNoYXBlID0gMTgsIHNpemUgPSAzLCBzaG93LmxlZ2VuZCA9IEZBTFNFKQoKZ2dwbG90X2JveCA8LSBmdW5jdGlvbihkYXRhLCB4Y29sdW1uLCB5Y29sdW1uLCBieWNvbHVtbiwgdGl0bGUsIHh0aXRsZSwgeXRpdGxlKSB7CiAgCiAgZGF0YSAlPiUKICAgIGdncGxvdChhZXMoeGNvbHVtbiwgeWNvbHVtbikpICsKICAgIGdlb21fYm94cGxvdChmaWxsID0gIndoaXRlIiwgY29sb3VyID0gImRhcmtibHVlIiwgCiAgICAgICAgICAgICAgICAgb3V0bGllci5jb2xvdXIgPSAicmVkIiwgb3V0bGllci5zaGFwZSA9IDEpICsKICAgIGdpdmVfY291bnQgKwogICAgZ2l2ZV9tZWFuICsKICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogICAgbGFicyh0aXRsZSA9IHRpdGxlLCB4ID0geHRpdGxlLCB5ID0geXRpdGxlKSArCiAgICBmYWNldF93cmFwKGJ5Y29sdW1uKQp9CmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBFREEKCmAKTCdvYmplY3RpZiBkZSBub3RyZSBFREEgbidlc3QgcGFzIGRlIHZpc3VhbGlzZXIgbGVzIDE1MSBjYXJhY3TDqXJpc3RpcXVlcyBkZSBub3RyZSBiYXNlIGRlIGRvbm7DqWVzLiBTdXIgbGEgYmFzZSBkdSBkaWN0aW9ubmFpcmUgZGUgZG9ubsOpZXMsIGRlIG5vcyBjb25uYWlzc2FuY2VzIHN1ciBsZSByaXNxdWUgZGUgY3LDqWRpdCBldCBkZXMgcmVjaGVyY2hlcyBxdWUgbm91cyBhdm9ucyBwdSBmYWlyZSwgbm91cyBhbGxvbnMgZXhwb3NlciB1bmUgYW5hbHlzZSBncmFwaGlxdWUgZXQgc3RhdGlzdGlxdWUgc3VyIGxlcyBjYXJhY3TDqXJpc3RpcXVlcyBxdWkgbm91cyBwYXJhaXNzZW50IGxlcyBwbHVzIGltcG9ydGFudGVzIGRhbnMgbCdhdWdtZW50YXRpb24gZGUgbGEgcHJvYmFiaWxpdMOpIGRlIGTDqWZhdXQuIFZvaWNpIHVuZSBsaXN0ZSBub24gZXhoYXVzdGl2ZSA6Ci0gTOKAmWhpc3RvcmlxdWUgZGVzIHJlbWJvdXJzZW1lbnRzIGR1IGNsaWVudCwgc+KAmWlsIGxlcyByw6hnbGVudCDDoCB0ZW1wcyBvdSBub24gOyAKLSBEZSBsYSBzaWduYWzDqXRpcXVlIHBlcnNvbm5lIChsaWV1IGRlIHLDqXNpZGVuY2UsIGxvY2F0YWlyZSBvdSBwcm9wcmnDqXRhaXJlLCBjYXTDqWdvcmllIHNvY2lvLXByb2Zlc3Npb25uZWxsZSwgZXRjLikgOwotIExlIG1vbnRhbnQgdG90YWwgZMO7IGV0IGxhIG1lbnN1YWxpdMOpIGFzc29jacOpZSA7IAotIExlIHNvbGRlIGNvdXJhbnQgOyAKLSBRdWFuZCBsYSBwcmVtacOocmUgbGlnbmUgZGUgY3LDqWRpdCBhLXQtZWxsZSDDqXTDqSBvdXZlcnRlIDsgCi0gTGUgdHlwZSBkZSBsaWduZSBkZSBjcsOpZGl0IChyZXZvbHZpbmcsIGh5cG90aMOpY2FpcmUpIDsKLSBMYSByYWlzb24gcG91ciBsYXF1ZWxsZSBs4oCZZW1wcnVudCBlc3QgY29udHJhY3TDqSA7Ci0gTGUgbm9tYnJlIGRlIGxpZ25lcyBkZSBjcsOpZGl0IHLDqWNlbW1lbnQgb3V2ZXJ0ZSBldCBsZSBub21icmUgdG90YWwgZGUgbGlnbmVzIHF1ZSBs4oCZZW1wcnVudGV1ciBwb3Nzw6hkZSA7IAotIExlIG5vbWJyZSBkZSBsaWduZXMgZGUgY3LDqWRpdCBvdXZlcnRlcyBxdWkgc29udCBlbiBkw6lmYXV0IG91IGdyYXZlIHJldGFyZCBkZSBwYWllbWVudCA7Ci0gTGUgbm9tYnJlIGTigJllbnF1w6p0ZXMgZGUgY3LDqWRpdCByw6ljZW50ZXMgOwotIExhIG5vdGUgZGUgY3LDqWRpdCBhdHRyaWJ1w6llIGF1IGRvc3NpZXIuCmAKCgojIyMjIFZhcmlhYmxlIDogImxvYW5fc3RhdHVzIgpJbCBzJ2FnaXQgZHUgc3RhdHV0IGFjdHVlbCBkdSBwcsOqdC4gQ2V0dGUgY2FyYWN0w6lyaXN0aXF1ZSBwcmVuZCBwbHVzaWV1cnMgbW9kYWxpdMOpcyA6ICB1biBwcsOqdCByZW1ib3Vyc8OpIG91IG5vbiwgZW4gY291cnMgb3UgZW4gcmV0YXJkIGRlIHBhaWVtZW50LgoKT2JzZXJ2YXRpb24gOiBvbiByZW1hcnF1ZSBxdWUgYmVhdWNvdXAgZGUgcHLDqnRzLCBkYW5zIGwnaGlzdG9yaXF1ZSBkZSBsYSBwbGF0ZWZvcm1lLCBzb250IHRvdGFsZW1lbnQgcmVtYm91cnPDqXMgKDcwLjglKS4gRW5zdWl0ZSBpbCB5IGEgdW5lIHBhcnQgbm9uIG7DqWdsaWdlYWJsZSBkZSBwcsOqdHMgcXVpIHNvbnQgZW4gY291cnMgZGUgcmVtYm91cnNlbWVudCAoMTAuNiUpIG1haXMgcXVpIHNvbnQsIMOgIGwnaGV1cmUsIG5vbiByZW1ib3Vyc8OpcywgcHVpcyBxdWFuZCBtw6ptZSB1bmUgYm9ubmUgcGFydCBkZSBwcsOqdHMgcXVpIG5lIHNvbnQgcGFzIGR1IHRvdXQgcmVtYm91cnPDqXMgKDE3LjklKS4gSWwgeSBhIHVuZSB0csOocyBmYWlibGUgbWlub3JpdMOpIGRlIHBhaWVtZW50cyBxdWkgc29udCBlbiByZXRhcmQgb3UgZW4gZ3LDomNlIGRlIHBhaWVtZW50IChtb2lucyBkZSAxJSkuCgpJbnR1aXRpb24gOiBjZXR0ZSB2YXJpYWJsZSBzZXJhIGxhIGNpYmxlIGRlIG5vcyBtb2TDqGxlcyBkZSBNYWNoaW5lIExlYXJuaW5nIHN1cGVydmlzw6lzLiBJbCB2YSBkb25jIGZhbGxvaXIgcmVzdHJ1Y3R1cmVyIGNldHRlIGNhcmFjdMOpcmlzdGlxdWUgYWZpbiBkJ2F2b2lyIHVuIHByb2Jsw6htZSBkZSBjbGFzc2lmaWNhdGlvbiDDoCAyIGNsYXNzZXMuIE5vdHJlIGTDqWZpbml0aW9uIGR1IGTDqWZhdXQgZGUgcGFpZW1lbnQgZXN0IGxhIHN1aXZhbnRlIDogc2kgbOKAmWVtcHJ1bnRldXIgbmUgcmVtYm91cnNlIHBhcyDDoCBsYSBkYXRlIGTigJnDqWNow6lhbmNlIGZpeMOpZSBwYXIgbGUgY29udHJhdCBhbG9ycyBpbCBlc3QgY29uc2lkw6lyw6kgZW4gZMOpZmF1dCBkZSBwYWllbWVudCwgZXQgY2VsYSBtw6ptZSBz4oCZaWwgcmVtYm91cnNlIHBsdXNpZXVycyBqb3VycyAvIG1vaXMgLyBhbm7DqWVzIGFwcsOocy4KQWluc2ksIGQndW4gY8O0dMOpIG5vdXMgYXVyb25zIGxlcyBlbXBydW50ZXVycyBheWFudCB0b3RhbGVtZW50IHJlbWJvdXJzw6lzIGxldXIgZW1wcnVudCBldCBkZSBsJ2F1dHJlLCBsZXMgZW1wcnVudGV1cnMgcmVzdGFudHMuIMOAIHByaW9yaSwgbGVzIGluZGl2aWR1cyBlbiBncsOiY2Ugc2Vyb250IGV4Y2x1cyBkZSBsYSBtb2TDqWxpc2F0aW9uIGNhciBpbHMgbidvbnQgcGFzIHJlbWJvdXJzw6kgbWFpcyBzb250IGVuIHDDqXJpb2RlIGRlIGdyw6JjZS4gRW4gb3V0cmUsIHBvdXIgbGVzIGVtcHJ1bnRzIGVuIGNvdXJzIGRlIHJlbWJvdXJzZW1lbnQgbm91cyBuZSBzYXZvbnMgcGFzIHNpIGwnZW1wcnVudGV1ciBlc3QgZW4gcmV0YXJkIG91IG5vbi4gQWluc2ksIG5vdXMgbGVzIMOpY2FydGVyb250IMOpZ2FsZW1lbnQuCgpgYGB7cn0KZGF0YSAlPiUgY291bnQobG9hbl9zdGF0dXMpCmBgYAoKYGBge3J9CmZpZyA8LSBwbG90X2x5KGRhdGEsIGxhYmVscyA9IH5sb2FuX3N0YXR1cywgdHlwZSA9ICJwaWUiLCBtYXJrZXIgPSBsaXN0KGNvbG9ycyA9IGMoIiNCQ0E5RjUiLCAiIzgyY2FhZiIsICIjQTlCQ0Y1IiwgIiNBOUY1QkMiLCAiI0Y3QkU4MSIsICIjRjc4MTgxIikpKQpmaWcgPC0gZmlnICU+JSBsYXlvdXQodGl0bGUgPSAiTG9hbiBzdGF0dXMgcHJvcG9ydGlvbiIsCiAgICAgICAgIHhheGlzID0gbGlzdChzaG93Z3JpZCA9IEZBTFNFLCB6ZXJvbGluZSA9IEZBTFNFLCBzaG93dGlja2xhYmVscyA9IEZBTFNFKSwKICAgICAgICAgeWF4aXMgPSBsaXN0KHNob3dncmlkID0gRkFMU0UsIHplcm9saW5lID0gRkFMU0UsIHNob3d0aWNrbGFiZWxzID0gRkFMU0UpKQoKZmlnCmBgYAoKUG91ciBjb250aW51ZXIgbm90cmUgZGF0YSB2aXN1YWxpc2F0aW9uLCBub3VzIGFsbG9ucyBjcsOpZXIgdW4gcHLDqWRpY2F0IG5vbW3DqSAiZGVmYXVsdCIgcXVpIG5vdXMgcGVybWV0dHJhIGRlIHZpc3VhbGlzZXIgcGx1cyBlbiBkw6l0YWlsIG5vcyBkaWZmw6lyZW50ZXMgY2FyYWN0w6lyaXN0aXF1ZXMgZW4gb3Bwb3NhbnQgbGVzIGVtcHJ1bnRldXJzIGVuIHNpdHVhdGlvbiBkZSBkw6lmYWlsbGFuY2UgZXQgbm9zIGVtcHJ1bnRldXJzIHNhaW5zLiBDZSByw6ktZW5jb2RhZ2Ugc2UgYmFzZSBzdXIgbm90cmUgaW50dWl0aW9uIHByw6ljw6lkZW50ZS4KCmBgYHtyfQpkYXRhMiA8LSBkYXRhCgpkYXRhMiA8LSBkYXRhMlshKGRhdGEyJGxvYW5fc3RhdHVzID09ICJDdXJyZW50IiB8IGRhdGEyJGxvYW5fc3RhdHVzID09ICJJbiBHcmFjZSBQZXJpb2QiKSwgXQoKZGVmYXVsdF92YXIgPC0gYygiQ2hhcmdlZCBPZmYiLCAiTGF0ZSAoMTYtMzAgZGF5cykiLCAiTGF0ZSAoMzEtMTIwIGRheXMpIikKCmRhdGEyIDwtIGRhdGEyICU+JQogIG11dGF0ZShkZWZhdWx0ID0gaWZlbHNlKCEobG9hbl9zdGF0dXMgJWluJSBkZWZhdWx0X3ZhciksIEZBTFNFLCBUUlVFKSkKCmRhdGEyICU+JQogIHN1bW1hcmlzZShkZWZhdWx0X2ZyZXEgPSBzdW0oZGVmYXVsdCAvIG4oKSkpCmBgYAoKTm91cyBwb3V2b25zIHZvaXIgcXVlIHN1aXRlIMOgIGNldHRlIGTDqWZpbml0aW9uIGV0IGNsYXNzaWZpY2F0aW9uIGR1IGTDqWZhdXQgZGUgcGFpZW1lbnQsIDIxJSBkZXMgZW1wcnVudGV1cnMgZGUgbm90cmUgYmFzZSBkZSBkb25uw6llcyBzb250IGNvbnNpZMOpcsOpcyBlbiBkw6lmYXV0IGRlIHBhaWVtZW50IHRhbmRpcyBxdWUgNzklIHNvbnQgZGVzIGVtcHJ1bnRldXJzIHNhaW5zLgoKYGBge3J9CnRhYmxlKGRhdGEyJGRlZmF1bHQpIC8gbnJvdyhkYXRhMikKYGBgCgoKIyMjIyBWYXJpYWJsZSA6ICJsb2FuX2Ftb3VudCIKCklsIHMnYWdpdCBkdSBtb250YW50IGR1IHByw6p0IGRlbWFuZMOpIHBhciBsJ2VtcHJ1bnRldXIgc3VyIGxhIHBsYXRlZm9ybWUuCgpPYnNlcnZhdGlvbiA6IGRhbnMgbCdlbnNlbWJsZSwgbGEgZGlzdHJpYnV0aW9uIGRlcyBtb250YW50cyBkZXMgcHLDqnRzIGRlbWFuZMOpcyBlc3QgbMOpZ8OocmVtZW50IMOpdGFsw6llIHZlcnMgbGEgZHJvaXRlLiBDZWNpIGluZGlxdWUgdW5lIHNrZXduZXNzIHBvc2l0aXZlICgwLjYxKSwgZG9uYyB1bmUgbW95ZW5uZSBzdXDDqXJpZXVyZSDDoCBsYSBtw6lkaWFuZS4gCi0gNTAlIGRlcyBwcsOqdHMgYWNjb3Jkw6lzIHNvbnQgZOKAmXVuIG1vbnRhbnQgaW5mw6lyaWV1ciDDoCAxNCAwMDDigqwgKHZhbGV1ciBkZSBsYSBtw6lkaWFuZSkuCi0gTGVzIG1vbnRhbnRzIGRlcyBkZW1hbmRlcyBkZSBmaW5hbmNlbWVudCBjb25uYWlzc2VudCB1biBwaWMgYXV0b3VyIGRlIGNoYXF1ZSBwYWxsaWVyIGRlIDUgMDAw4oKsLgotIERlIG1hbmnDqHJlIGfDqW7DqXJhbGUsIGxlcyBwcsOqdHMgdG90YWxlbWVudCByZW1ib3Vyc8OpcyBzb250IGNldXggcG91ciBsZXNxdWVscyBsZSBtb250YW50IG1veWVuIGR1IHByw6p0IGVzdCBsZSBwbHVzIGZhaWJsZS4gUG91ciB0b3V0ZXMgbGVzIGF1dHJlcyBtb2RhbGl0w6lzIGR1IHN0YXR1dCBkdSBwcsOqdCwgbGVzIG1vbnRhbnRzIG1veWVucyBkZSBmaW5hbmNlbWVudCBzb250IHN1cMOpcmlldXJzLgotIFBhcyBkJ291dGxpZXJzIHBvdXIgY2V0dGUgdmFyaWFibGUuIExlcyBtb250YW50cyBtaW5pbXVtcyBldCBtYXhpbXVtcyBzb250IHRvdXQgw6AgZmFpdCBub3JtYXV4IGV0IHBsdXTDtHQgw6lxdWlsaWJyw6lzIGVudHJlIGxlcyBkaWZmw6lyZW50ZXMgY2xhc3Nlcy4gU2F1ZiBwb3VyIGRldXggY2xhc3Nlcy4gQ2VjaSBwZXV0IHZlbmlyIGR1IGZhaXQgcXUnaWwgcydhZ2lzc2UgZCd1biBzYW1wbGUgZXQgbm9uIGR1IGRhdGFzZXQgdG90YWwuCgpgYGB7cn0KZmlnIDwtIHBsb3Rfc3VicGxvdChkYXRhLCBkYXRhJGxvYW5fYW1udCwgZGF0YSRsb2FuX3N0YXR1cywgIkxvYW4gYW1vdW50IHZhcmlhYmxlIiwgIkxvYW4gYW1vdW50IiwgIkNvdW50IikKZmlnCmBgYAoKYGBge3J9CnN0YXRfZGVzYyA8LSBkZXNjcmlwdGl2ZV9zdGF0aXN0aWMxKGRhdGEsIGRhdGEkbG9hbl9hbW50KQpzdGF0X2Rlc2MKYGBgCgpgYGB7cn0KZGF0YSAlPiUKICBncm91cF9ieShsb2FuX3N0YXR1cykgJT4lCiAgc3VtbWFyaXNlKE1pbmltdW0gPSByb3VuZChtaW4obG9hbl9hbW50KSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgIE1heGltdW0gPSByb3VuZChtYXgobG9hbl9hbW50KSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgIE1veWVubmUgPSByb3VuZChtZWFuKGxvYW5fYW1udCksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBNZWRpYW5lID0gcm91bmQobWVkaWFuKGxvYW5fYW1udCksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBWYXJpYW5jZSA9IHJvdW5kKHZhcihsb2FuX2FtbnQpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgRWNhcnRfdHlwZSA9IHJvdW5kKHNkKGxvYW5fYW1udCksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBLdXJ0b3NpcyA9IHJvdW5kKGt1cnRvc2lzKGxvYW5fYW1udCksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBTa2V3bmVzcyA9IHJvdW5kKHNrZXduZXNzKGxvYW5fYW1udCksIGRpZ2l0cyA9IDQpKQpgYGAKClF1aWQgZHUgbW9udGFudCBvY3Ryb3nDqSBwb3VyIGxlIHByw6p0IGVuIGZvbmN0aW9uIGR1IGdyYWRlIGRlIGNyw6lkaXQgTGVuZGluZyBDbHViIGRlIGwnZW1wcnVudGV1ciA/IE5vdXMgcmVtYXJxdW9ucyBxdWUgbGUgbW9udGFudCBvY3Ryb3nDqSBhIHRlbmRhbmNlIMOgIGF1Z21lbnRlciBhdmVjIGxlcyBncmFkZXMgZGUgY3LDqWRpdCBMZW5kaW5nIENsdWIuIEMnZXN0LcOgLWRpcmUgcXVlIGxlcyBncmFkZXMgbGVzIHBsdXMgcmlzcXXDqXMgc29udCBjZXV4IHF1aSBlbXBydW50ZW50IGxlcyBwbHVzIGdyYW5kcyBtb250YW50cyBlbiBtb3llbm5lLgpQb3VyIGxhIGRpc3RpbmN0aW9uIGVudHJlIGxlcyBjbGFzc2VzLCBvbiB2b2l0IHF1ZSBsZXMgZW1wcnVudGV1cnMgZW4gZMOpZmF1dCAoVFJVRSkgZW1wcnVudGVudCwgZW4gbW95ZW5uZSwgdHLDqHMgbMOpZ8OocmVtZW50IHBsdXMgcXVlIGxlcyBlbXBydW50ZXVycyBzYWlucyAoRkFMU0UpLgoKYGBge3J9CmdncGxvdF9ib3goZGF0YTIsIGRhdGEyJGdyYWRlLCBkYXRhMiRsb2FuX2FtbnQsIGRhdGEyJGRlZmF1bHQsICJMb2FuIEFtb3VudCBieSBHcmFkZSIsICJHcmFkZSIsICJMb2FuIGFtb3VudCBcbiIpCmBgYAoKUXVpZCBkdSBtb250YW50IG9jdHJvecOpIHBvdXIgbGUgcHLDqnQgZW4gZm9uY3Rpb24gZGUgbGEgdsOpcmlmaWNhdGlvbiBkZSBsYSBzb3VyY2UgZGUgcmV2ZW51IGFubnVlbCBkZSBsJ2VtcHJ1bnRldXIgPyBMZXMgbW9udGFudHMgbW95ZW5zIGVtcHJ1bnTDqXMgc29udCByZWxhdGl2ZW1lbnQgcHJvY2hlcyBwb3VyIGxlcyB0cm9pcyBtb2RhbGl0w6lzIGRlIGxhIHZhcmlhYmxlICJ2ZXJpZmljYXRpb25fc3RhdHVzIi4KCmBgYHtyfQpkYXRhMiAlPiUKICBnZ3Bsb3QoYWVzKHZlcmlmaWNhdGlvbl9zdGF0dXMsIGxvYW5fYW1udCkpICsKICBnZW9tX2JveHBsb3QoZmlsbCA9ICJ3aGl0ZSIsIGNvbG91ciA9ICJkYXJrYmx1ZSIsIAogICAgICAgICAgICAgICBvdXRsaWVyLmNvbG91ciA9ICJyZWQiLCBvdXRsaWVyLnNoYXBlID0gMSkgKwogIGdpdmVfY291bnQgKwogIGdpdmVfbWVhbiArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgbGFicyh0aXRsZT0iTG9hbiBBbW91bnQgYnkgdmVyaWZpY2F0aW9uIHN0YXR1cyIsIHggPSAiVmVyaWZpY2F0aW9uIHN0YXR1cyIsIHkgPSAiTG9hbiBhbW91bnQgXG4iKQpgYGAKCgojIyMjIFZhcmlhYmxlIDogImZ1bmRlZF9hbW50IgoKQ09NTUVOVEFJUkVTLgoKYGBge3J9CmZpZyA8LSBwbG90X3N1YnBsb3QoZGF0YSwgZGF0YSRmdW5kZWRfYW1udCwgZGF0YSRsb2FuX3N0YXR1cywgIkZ1bmRlZCBhbW91bnQgdmFyaWFibGUiLCAiRnVuZGVkIGFtb3VudCIsICJDb3VudCIpCmZpZwpgYGAKCmBgYHtyfQpzdGF0X2Rlc2MgPC0gZGVzY3JpcHRpdmVfc3RhdGlzdGljMShkYXRhLCBkYXRhJGZ1bmRlZF9hbW50KQpzdGF0X2Rlc2MKYGBgCgpgYGB7cn0KZGF0YSAlPiUKICBncm91cF9ieShsb2FuX3N0YXR1cykgJT4lCiAgc3VtbWFyaXNlKE1pbmltdW0gPSByb3VuZChtaW4oZnVuZGVkX2FtbnQpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgTWF4aW11bSA9IHJvdW5kKG1heChmdW5kZWRfYW1udCksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBNb3llbm5lID0gcm91bmQobWVhbihmdW5kZWRfYW1udCksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBNZWRpYW5lID0gcm91bmQobWVkaWFuKGZ1bmRlZF9hbW50KSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgIFZhcmlhbmNlID0gcm91bmQodmFyKGZ1bmRlZF9hbW50KSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgIEVjYXJ0X3R5cGUgPSByb3VuZChzZChmdW5kZWRfYW1udCksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBLdXJ0b3NpcyA9IHJvdW5kKGt1cnRvc2lzKGZ1bmRlZF9hbW50KSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgIFNrZXduZXNzID0gcm91bmQoc2tld25lc3MoZnVuZGVkX2FtbnQpLCBkaWdpdHMgPSA0KSkKYGBgCgoKIyMjIyBWYXJpYWJsZSA6ICJpbnRfcmF0ZSIKCkMnZXN0IGxlIHRhdXggZCdpbnTDqXLDqnQgZHUgcHLDqnQuCgpPYnNlcnZhdGlvbiA6IGdsb2JhbGVtZW50LCBsZSB0YXV4IGQnaW50w6lyw6p0IHZhcmllIGRlIDUlIMOgIDMwJSBhdmVjIHVuZSBkaXN0cmlidXRpb24gcGx1dMO0dCBjZW50csOpZSDDoCBnYXVjaGUgbWFpcyBiaWVuIMOpdGFsw6llIMOgIGRyb2l0ZSAoc2tld25lc3MgcG9zaXRpdmUgOiAwLjY1KS4gT24gcmVtYXJxdWUgdW4gcGljIGF1eCBhbGVudG91cnMgZGUgMTIvMTMlIChtb3llbm5lIMOgIDEyLjIlKSBwdWlzIHVuZSBkw6ljcm9pc3NhbmNlIGxlbnRlIGp1c3F1J2F1IG1heGltdW0gZGUgbGEgZGlzdHJpYnV0aW9uICgyOC45OSUpLgpRdWlkIGRlcyBkaWZmw6lyZW5jZXMgaW50ZXItY2xhc3NlID8gTGVzIHRhdXggZCdpbnTDqXLDqnQgcmVzdGVudCByZWxhdGl2ZW1lbnQgcHJvY2hlIG7DqWFubW9pbnMgbGEgbW95ZW5uZSBkZXMgZW1wcnVudGV1cnMgYXlhbnQgcmVtYm91cnPDqXMgZXN0IGRlIDExLjQlIHRhbmRpcyBxdWUgY2VsbGUgZGVzIGTDqWZhaWxsYW50cyBhaW5zaSBxdWUgZGVzIGF1dHJlcyBjbGFzc2VzIGVsbGUgZXN0IHN1cMOpcmlldXJlIMOgIDE0JS4gTm91cyByZXRyb3V2b25zIGNldHRlIG3Dqm1lIGRpc3RpbmN0aW9uIGF1IG5pdmVhdSBkZSBsYSBtw6lkaWFuZSBkZXMgZGlzdHJpYnV0aW9ucy4gQ2VjaSBlc3QgcGx1dMO0dCBsb2dpcXVlLCBzaSBsZSByaXNxdWUgZGUgZMOpZmF1dCBhdWdtZW50ZSwgbGUgdGF1eCBkJ2ludMOpcsOqdCBhdWdtZW50ZSBhdXNzaS4gTm91cyBjb25jbHVvbnMgcXVlLCBlbiBtb3llbm5lLCBwbHVzIGxlIHRhdXggZCdpbnTDqXLDqnQgZXN0IGZvcnQsIG1vaW5zIGxlIHByw6p0IGVzdCByZW1ib3Vyc8OpLgoKTm90b25zIHF1J2lsIHNlbWJsZSB5IGF2b2lyIHBsdXNpZXVycyBvdXRsaWVycyBwb3VyIGxlcyB0cm9pcyBwcmVtacOocmVzIGNsYXNzZXMgZHUgZ3JhcGhpcXVlLiBDZWxhIHNlcmEgw6AgdHJhaXRlciBkYW5zIGxlIHByZS1wcm9jZXNzaW5nLgoKQ2ktZGVzc291cywgbm91cyBhbGxvbnMgYW5hbHlzZXIgYXZlYyBkZSBub21icmV1eCBkw6l0YWlscyBjZXR0ZSB2YXJpYWJsZSBxdWkgZXN0IHVuZSByw6llbGxlIG1lc3VyZSBkZSByaXNxdWUgZW4gw6ljb25vbWllIG1vbsOpdGFpcmUgZXQgYmFuY2FpcmUuCgpgYGB7cn0KZmlnIDwtIHBsb3Rfc3VicGxvdChkYXRhLCBkYXRhJGludF9yYXRlLCBkYXRhJGxvYW5fc3RhdHVzLCAiSW50ZXJlc3QgcmF0ZSB2YXJpYWJsZSIsICJJbnRlcmVzdCByYXRlIiwgIkNvdW50IikKZmlnCmBgYAoKYGBge3J9CnN0YXRfZGVzYyA8LSBkZXNjcmlwdGl2ZV9zdGF0aXN0aWMxKGRhdGEsIGRhdGEkaW50X3JhdGUpCnN0YXRfZGVzYwpgYGAKCmBgYHtyfQpkYXRhICU+JQogIGdyb3VwX2J5KGxvYW5fc3RhdHVzKSAlPiUKICBzdW1tYXJpc2UoTWluaW11bSA9IHJvdW5kKG1pbihpbnRfcmF0ZSksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBNYXhpbXVtID0gcm91bmQobWF4KGludF9yYXRlKSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgIE1veWVubmUgPSByb3VuZChtZWFuKGludF9yYXRlKSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgIE1lZGlhbmUgPSByb3VuZChtZWRpYW4oaW50X3JhdGUpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgVmFyaWFuY2UgPSByb3VuZCh2YXIoaW50X3JhdGUpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgRWNhcnRfdHlwZSA9IHJvdW5kKHNkKGludF9yYXRlKSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgIEt1cnRvc2lzID0gcm91bmQoa3VydG9zaXMoaW50X3JhdGUpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgU2tld25lc3MgPSByb3VuZChza2V3bmVzcyhpbnRfcmF0ZSksIGRpZ2l0cyA9IDQpKQpgYGAKClF1aWQgZGUgbCfDqXZvbHV0aW9uIGRlcyB0YXV4IGQnaW50w6lyw6p0IHNlbG9uIGwnw6ljaMOpYW5jZSBkdSBwcsOqdCBldCBsZSBncmFkZSBkZSBjcsOpZGl0IExlbmRpbmcgQ2x1YiBkZSBsJ2VtcHJ1bnRldXIgPwpDJ2VzdCBhc3NleiBwZXJ0dXJiYW50LCBub3VzIG5vdXMgYXR0ZW5kaW9ucyDDoCBvYnNlcnZlciB1bmUgcGx1cyBmb3J0ZSBkaXN0aW5jdGlvbiBlbnRyZSBsZXMgZGlmZsOpcmVudGVzIMOpY2jDqWFuY2VzLiBFbiBlZmZldCwgZW4gw6ljb25vbWllIGxvZ2lxdWVtZW50IHBsdXMgbCdob3Jpem9uIHRlbXBvcmVsbGUgKGkuZS4gaWNpIGwnw6ljaMOpYW5jZSkgZXN0IGxvaW50YWluIGV0IHBsdXMgbGVzIHJpc3F1ZXMgc29udCDDqWxldsOpcy4gQWluc2ksIG5vcm1hbGVtZW50IGxlIHRhdXggZCdpbnTDqXLDqnQgw6AgbG9uZy10ZXJtZSAoNjAgbW9pcykgZGV2cmFpdCDDqnRyZSBzdXDDqXJpZXVyIMOgIGNlbHVpIGQndW4gZW1wcnVudCBzdXIgMzYgbW9pcyBjYXIgbGUgcmlzcXVlIGRlIGTDqWZhdXQgZXN0IHBsdXMgZ3JhbmQgw6AgbG9uZy10ZXJtZS4KR2xvYmFsZW1lbnQsIGxlcyB0YXV4IGQnaW50w6lyw6p0IHNvbnQgc2ltaWxhaXJlcyBwb3VyIGNoYXF1ZSBncmFkZSBkZSBjcsOpZGl0IGV0IGNlbGEgcGV1IGltcG9ydGUgbCfDqWNow6lhbmNlIGR1IHByw6p0LgoKYGBge3J9CmRhdGEyICU+JQogIGdncGxvdChhZXMoZ3JhZGUsIGludF9yYXRlKSkgKwogIGdlb21fYm94cGxvdChmaWxsID0gIndoaXRlIiwgY29sb3VyID0gImRhcmtibHVlIiwgCiAgICAgICAgICAgICAgIG91dGxpZXIuY29sb3VyID0gInJlZCIsIG91dGxpZXIuc2hhcGUgPSAxKSArCiAgZ2l2ZV9jb3VudCArCiAgZ2l2ZV9tZWFuICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBsYWJzKHRpdGxlPSJJbnRlcmVzdCBSYXRlIGJ5IEdyYWRlIiwgeCA9ICJHcmFkZSIsIHkgPSAiSW50ZXJlc3QgUmF0ZSBcbiIpICsKICBmYWNldF93cmFwKH4gdGVybSkKYGBgCgojIyMjIFRhdXggZCdpbnTDqXLDqnQgbW95ZW4gZXQgbcOpZGlhbiBwYXIgZ3JhZGUgZGUgY3LDqWRpdCBMZW5kaW5nIGNsdWIKYGBge3J9CmRhdGEyICU+JQogIHNlbGVjdChpbnRfcmF0ZSwgZ3JhZGUpICU+JQogIGdyb3VwX2J5KGdyYWRlKSAlPiUKICBzdW1tYXJpc2UoaW50X3JhdGVfbWVhbiA9IG1lYW4oaW50X3JhdGUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIGludF9yYXRlX21lZGlhbiA9IG1lZGlhbihpbnRfcmF0ZSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgbiA9IG4oKSkKYGBgCgpRdWlkIGR1IHRhdXggZCdpbnTDqXLDqnQgc2Vsb24gbGUgc3RhdHV0IGRlIGwnZW1wcnVudGV1ciBldCBzYSBjYXJhY3TDqXJpc3RpcXVlICJvd25lcl9zaGlwIiA/CkxhIHByZW1pw6hyZSBjaG9zZSDDoCByZWxldmVyIGVzdCBsZSBmYWl0IHF1ZSBsZXMgdGF1eCBkJ2ludMOpcsOqdCBzb250IGJpZW4gcGx1cyDDqWxldsOpcyBwb3VyIGxlcyBlbXBydW50ZXVycyBlbiBzaXR1YXRpb24gZGUgZMOpZmFpbGxhbmNlLiBEYW5zIHVuIHNlY29uZCB0ZW1wcywgbm91cyByZW1hcnF1b25zIHF1ZSBsZXMgZW1wcnVudGV1cnMgcXVpIHNvbnQgZW4gbG9jYXRpb24gaW1tb2JpbGnDqHJlIHBvc3PDqGRlbnQgdW4gdGF1eCBkJ2ludMOpcsOqdCBtb3llbiBwbHVzIMOpbGV2w6kgcXVlIGNldXggw6l0YW50IHByb3ByacOpdGFpcmVzIG91IGVuIGh5cG90aMOocXVlcy4KRGUgbWFuacOocmUgw6ljb25vbWlxdWUgY2VsYSBwZXV0IMOqdHJlIGNvaMOpcmVudCBjYXIgbGEgYmFucXVlIG91IGwnaW52ZXN0aXNzZXVyIHNhaXQgcXVlIHNpIGNldCBlbXBydW50ZXVyIG5lIHBldXQgcmVtYm91cnNlciBzb24gZW1wcnVudCwgaWwgcG9zc8OoZGUgdG91am91cnMgdW4gY2VydGFpbiBwYXRyaW1vaW5lIHBvdXZhbnQgc2VydmlyIMOgIHJlbWJvdXJzZXIgc2VzIGRldHRlcy4KCmBgYHtyfQpnZ3Bsb3RfYm94KGRhdGEyLCBkYXRhMiRob21lX293bmVyc2hpcCwgZGF0YTIkaW50X3JhdGUsIGRhdGEyJGRlZmF1bHQsICJJbnRlcmVzdCBSYXRlIGJ5IEhvbWUgT3duZXJzaGlwIiwgIkhvbWUgT3duZXJzaGlwIiwgIkludGVyZXN0IHJhdGUgXG4iKQpgYGAKClF1aWQgZHUgdGF1eCBkJ2ludMOpcsOqdCBzZWxvbiBsZSBzdGF0dXQgZGUgbCdlbXBydW50ZXVyIGV0IHNhIGNhcmFjdMOpcmlzdGlxdWUgInB1cnBvc2UiID8KQ29tbWUgcHLDqWPDqWRlbW1lbnQsIG5vdXMgbm90b25zIHF1ZSBsZXMgdGF1eCBkJ2ludMOpcsOqdCBtb3llbnMgc29udCBsw6lnw6hyZW1lbnQgcGx1cyDDqWxldsOpcyBwb3VyIGxlcyBjbGllbnRzIGVuIGTDqWZhdXQgZGUgcGFpZW1lbnQuCkxhIHF1ZXN0aW9uIHF1ZSBsJ29uIHNlIHBvc2UgaWNpIGVzdCBkZSBzYXZvaXIgc2kgbGUgZmFpdCBkJ2VtcHJ1bnRlciBwb3VyIHVuIG9iamVjdGlmIHByw6ljaXMgYXVnbWVudGUgbGEgcHJvYmFiaWxpdMOpIGRlIGZhaXJlIGTDqWZhdXQgZXQgc3VydG91dCBzaSwgcG91ciB1biBtw6ptZSBvYmplY3RpZiBkJ2ludmVzdGlzc2VtZW50LCBpbCB5IGEgdW5lIGRpZmbDqXJlbmNlIGludGVyLWNsYXNzZS4gQydlc3QgYmllbiBjZSBxdWUgbm91cyByZW1hcnF1b25zLiBMZSBmYWl0IGQnZW1wcnVudGVyIHBvdXIgbGEgbW9kYWxpdMOpICJzbWFsbCBidXNpbmVzcyIgKGVtcHJ1bnQgcG91ciBsYSBjcsOpYXRpb24gZXQgbGUgbGFuY2VtZW50IGRlIHNvbiBlbnRyZXByaXNlKSBhY2Nyb8OudCB0csOocyBmb3J0ZW1lbnQgbGUgdGF1eCBkJ2ludMOpcsOqdCBldCBjZWxhIHBldSBpbXBvcnRlIGxlIHBhdHRlcm4gZGUgZMOpZmF1dCBvdSBub24gZGUgbCdlbXBydW50ZXVyLiBMZSBzZWNvbmQgbW90aWYgZmFpc2FudCBhY2Nyb8OudHJlIGxlIHRhdXggZCdpbnTDqXLDqnQgZXN0IGwnZW1wcnVudCBwb3VyIGwnYWNoYXQgZCd1bmUgbWFpc29uLiBDZWNpIGVzdCBjb2jDqXJlbnQgYXZlYyBsZSBmYWl0IHF1ZSBub3VzIGF2aW9ucyB2dSBxdWUgbGVzIGNsaWVudHMgbG9jYXRhaXJlcyBhdmFpZW50IHVuIHRhdXggZCdpbnTDqXLDqnQgcGx1cyDDqWxldsOpIHNhY2hhbnQgcXVlIGMnZXN0IHBvdGVudGllbGxlbWVudCBjZXMgZGVybmllcnMgcXVpIGNvbnRyYWN0ZW50IGRlcyBwcsOqdHMgcG91ciBsJ2FjaGF0IGQndW5lIG1haXNvbi4KCkdsb2JhbGVtZW50IG5vdXMgcmVtYXJxdW9ucyBiaWVuIHF1ZSBsZXMgdGF1eCBkJ2ludMOpcsOqdCBwb3Nzw6hkZW50IHVuIHBvdGVudGllbCBwb3V2b2lyIGRpc2NyaW1pbmFudCBhZmluIGRlIGRpZmbDqXJlbmNpZXIgbGVzIGVtcHJ1bnRldXJzIGVuIGTDqWZhdXQgb3Ugbm9uLgoKYGBge3J9CmRhdGEyICU+JQogIGdncGxvdChhZXMocHVycG9zZSwgaW50X3JhdGUpKSArCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAid2hpdGUiLCBjb2xvdXIgPSAiZGFya2JsdWUiLCAKICAgICAgICAgICAgICAgb3V0bGllci5jb2xvdXIgPSAicmVkIiwgb3V0bGllci5zaGFwZSA9IDEpICsKICBnaXZlX2NvdW50ICsKICBnaXZlX21lYW4gKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGZhY2V0X3dyYXAofiBkZWZhdWx0KSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSkpCiAgbGFicyh0aXRsZT0iSW50ZXJlc3QgUmF0ZSBieSBMb2FuIFB1cnBvc2UiLCB4ID0gIkxvYW4gcHVycG9zZSIsIHkgPSAiSW50ZXJlc3QgcmF0ZSBcbiIpCmBgYAoKUXVpZCBkdSB0YXV4IGQnaW50w6lyw6p0IHNlbG9uIGxlIHN0YXR1dCBkZSBsJ2VtcHJ1bnRldXIgZXQgc2EgY2FyYWN0w6lyaXN0aXF1ZSAidmVyaWZpY2F0aW9uX3N0YXR1cyIgPwpDb21tZSBwcsOpY8OpZGVtbWVudCwgbGUgdGF1eCBkJ2ludMOpcsOqdCBhdWdtZW50ZSBzZWxvbiBkZSBzdGF0dXQgZGUgbCdlbXBydW50ZXVyIChUUlVFIG91IEZMQVNFKS4gTGVzIHRhdXggZCdpbnTDqXLDqnQgc29udCBwbHVzIGZhaWJsZXMsIGVuIG1veWVubmUsIHBvdXIgbGVzIGVtcHJ1bnRldXJzIGRvbnQgbCdhdXRvLWTDqWNsYXJhdGlvbiBkdSByZXZlbnUgbidhIHBhcyDDqXTDqSB2w6lyaWZpw6llLgoKYGBge3J9CmdncGxvdF9ib3goZGF0YTIsIGRhdGEyJHZlcmlmaWNhdGlvbl9zdGF0dXMsIGRhdGEyJGludF9yYXRlLCBkYXRhMiRkZWZhdWx0LCAiSW50ZXJlc3QgUmF0ZSBieSBWZXJpZmljYXRpb24gU3RhdHVzIiwgIlZlcmlmaWNhdGlvbiBzdGF0dXMiLCAiSW50ZXJlc3QgcmF0ZSBcbiIpCmBgYAoKCiMjIyMgVmFyaWFibGUgOiAiaW5zdGFsbG1lbnQiCgpJbCBzJ2FnaXQgZGUgbGEgbWVuc3VhbGl0w6kgZHVlIHBhciBsJ2VtcHJ1bnRldXIgc2kgbGUgcHLDqnQgZXN0IGFjY29yZMOpLgoKT2JzZXJ2YXRpb24gOiBsZSBtb250YW50IGRlcyB2ZXJzZW1lbnRzIHZhcmllIGxhcmdlbWVudCwgYXZlYyB1bmUgZGlzdHJpYnV0aW9uIGFzc2V6IMOpdGFsw6llIHZlcnMgbGEgZHJvaXRlIGNlIHF1aSBtb250cmUgbGEgcHLDqXNlbmNlIGRlIHRyw6hzIGdyb3NzZXMgbWVuc3VhbGl0w6lzIChtZW5zdWFsaXTDqSBtYXhpbWFsZSA6IDEzNTQuNjYkKS4gQXZlYyBsZXMgYm94cGxvdCwgb24gbmUgdm9pdCBwYXMgZGUgZGlmZsOpcmVuY2Ugc2lnbmlmaWNhdGl2ZSBlbnRyZSBsZXMgbWVuc3VhbGl0w6lzIGQndW4gZW1wcnVudGV1ciBxdWkgYSB0b3RhbGVtZW50IHJlbWJvdXJzw6kgc29uIGNyw6lkaXQgcGFyIHJhcHBvcnQgw6AgY2VsdWkgZW4gZMOpZmF1dCBzYXVmIHBvdXIgbGVzIG1lbnN1YWxpdMOpcyBtaW5pbWFsZXMgZXQgbWF4aW1hbGVzLgpEYW5zIGwnZW5zZW1ibGUsIGxlcyBtZW5zdWFsaXTDqXMgbW95ZW5uZXMgZGVzIGNsYXNzZXMgc29udCByZWxhdGl2ZW1lbnQgcHJvY2hlcy4gRm9yY8OpbWVudCwgYydlc3QgbGVzIGVtcHJ1bnRldXJzIHF1aSBwb3Nzw6hkZW50IGxlcyBwbHVzIGZhaWJsZXMgbWVuc3VhbGl0w6lzIGVuIG1veWVubmUuIFBvdXIgbGEgbcOpZGlhbmUsIG9uIG9ic2VydmUgbGEgbcOqbWUgY2hvc2UgYXZlYyB1bmUgZGlzdGluY3Rpb24gaW50ZXItY2xhc3NlIGzDqWfDqHJlbWVudCBwbHVzIGZvcnRlLiBFbiByZXZhbmNoZSwgYydlc3QgdW4gZW1wcnVudGV1ciBxdWkgYSBlZmZlY3RpdmVtZW50IHJlbWJvdXJzw6kgc29uIGVtcHJ1bnQgcXVpIGTDqXRpZW50IGxhIG1lbnN1YWxpdMOpIGxhIHBsdXMgw6lsZXbDqWUgZGUgbm90cmUgw6ljaGFudGlsbG9uLgpVbmUgZm9pcyBlbmNvcmUsIHBsdXNpZXVycyBvdXRsaWVycyBzb250IGlkZW50aWZpw6lzIGF1IHNlaW4gZGUgY2V0dGUgY2FyYWN0w6lyaXN0aXF1ZS4gTm91cyB2ZXJyb25zIGxvcnMgZHUgcHJlLXByb2Nlc3NzaW5nIGNvbW1lbnQgbGVzIHRyYWl0ZXIuCgpgYGB7cn0KZmlnMSA8LSBwbG90X2x5KGRhdGEsIHggPSB+ZGF0YSRpbnN0YWxsbWVudCwgdHlwZSA9ICJoaXN0b2dyYW0iLCBtYXJrZXIgPSBsaXN0KGNvbG9yID0gIiNCQ0E5RjUiKSkKZmlnMiA8LSBwbG90X2x5KGRhdGEsIHkgPSB+ZGF0YSRpbnN0YWxsbWVudCwgeCA9IH5kYXRhJGxvYW5fc3RhdHVzLCB0eXBlID0gImJveCIsIG1hcmtlciA9IGxpc3QoY29sb3IgPSAiIzgyY2FhZiIpKQoKZmlnIDwtIHN1YnBsb3QoZmlnMSwgZmlnMiwgbnJvd3MgPSAyKSAKZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gIkluc3RhbGxtZW50IHZhcmlhYmxlIiwKICAgICAgICAgcGxvdF9iZ2NvbG9yPSIjRkZGRkZGIiwgCiAgICAgICAgIHhheGlzID0gbGlzdCgKICAgICAgICAgICB0aXRsZSA9ICJJbnN0YWxsbWVudCIsCiAgICAgICAgICAgemVyb2xpbmVjb2xvciA9ICIjZmZmZiIsIAogICAgICAgICAgIHplcm9saW5ld2lkdGggPSAyLCAKICAgICAgICAgICBncmlkY29sb3IgPSAiZmZmZiIpLCAKICAgICAgICAgeWF4aXMgPSBsaXN0KAogICAgICAgICAgIHRpdGxlID0gIkNvdW50IiwKICAgICAgICAgICB6ZXJvbGluZWNvbG9yID0gIiNmZmZmIiwgCiAgICAgICAgICAgemVyb2xpbmV3aWR0aCA9IDIsIAogICAgICAgICAgIGdyaWRjb2xvciA9ICJmZmZmIiksIAogICAgICAgICBzaG93bGVnZW5kID0gRkFMU0UsCiAgICAgICAgIHNob3dsZWdlbmQyID0gRkFMU0UpCgpmaWcKYGBgCgpgYGB7cn0Kc3RhdF9kZXNjIDwtIGRlc2NyaXB0aXZlX3N0YXRpc3RpYzEoZGF0YSwgZGF0YSRpbnN0YWxsbWVudCkKc3RhdF9kZXNjCmBgYAoKYGBge3J9CmRhdGEgJT4lCiAgZ3JvdXBfYnkobG9hbl9zdGF0dXMpICU+JQogIHN1bW1hcmlzZShNaW5pbXVtID0gcm91bmQobWluKGluc3RhbGxtZW50KSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgIE1heGltdW0gPSByb3VuZChtYXgoaW5zdGFsbG1lbnQpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgTW95ZW5uZSA9IHJvdW5kKG1lYW4oaW5zdGFsbG1lbnQpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgTWVkaWFuZSA9IHJvdW5kKG1lZGlhbihpbnN0YWxsbWVudCksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBWYXJpYW5jZSA9IHJvdW5kKHZhcihpbnN0YWxsbWVudCksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBFY2FydF90eXBlID0gcm91bmQoc2QoaW5zdGFsbG1lbnQpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgS3VydG9zaXMgPSByb3VuZChrdXJ0b3NpcyhpbnN0YWxsbWVudCksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBTa2V3bmVzcyA9IHJvdW5kKHNrZXduZXNzKGluc3RhbGxtZW50KSwgZGlnaXRzID0gNCkpCmBgYAoKUXVpZCBkZSBsYSBtZW5zdWFsaXTDqSBzZWxvbiBsZSBtb250YW50IGVtcHJ1bnTDqSBldCBsYSBzdGF0dXQgZGUgbCdlbXBydW50ZXVyID8KCmBgYHtyfQpnZ3Bsb3RfYm94KGRhdGEyLCBkYXRhMiRpbnN0YWxsbWVudCwgZGF0YTIkbG9hbl9hbW50LCBkYXRhMiRkZWZhdWx0LCAiTG9hbiBhbW91bnQgYnkgSW5zdGFsbG1lbnQiLCAiSW5zdGFsbG1lbnQiLCAiTG9hbiBhbW91bnQgXG4iKQpgYGAKCgojIyMjIFZhcmlhYmxlcyBjYXTDqWdvcmllbGxlcwoKTm91cyBhbGxvbnMgZmFpcmUgdW4gZm9jdXMgc3VyIGxlcyBkaXN0cmlidXRpb25zIGRlcyBkaWZmw6lyZW50ZXMgbW9kYWxpdMOpcyBwb3VyIDYgdmFyaWFibGVzIHF1YWxpdGF0aXZlcy4KLSAiaG9tZV9vd25lcnNoaXAiCi0gInRlcm0iCi0gInZlcmlmaWNhdGlvbl9zdGF0dXMiCi0gInB1cnBvc2UiCi0gImdyYWRlIgotICJwdWJfcmVjX2JhbmtydXB0Y2llcyIKCk9ic2VydmF0aW9ucyA6Ci0gTGEgZ3JhbmRlIG1ham9yaXTDqSBkZXMgcGVyc29ubmVzIGRlbWFuZGFudCBkZXMgcHLDqnRzIHNvbnQgZGVzIHBlcnNvbm5lcyDDqXRhbnQgZW4gbG9jYXRpb24gb3UgZW4gaHlwb3Row6hxdWUgPT4gZW1wcnVudGV1cnMgZW4gc2l0dWF0aW9uIHByw6ljYWlyZS4KLSBMYSB0csOocyBncmFuZGUgbWFqb3JpdMOpIGRlcyBwcsOqdHMgZmluYW5jw6lzIHN1ciBsYSBwbGF0ZWZvcm1lIHNvbnQgZCd1bmUgZHVyw6llIGRlIDM2IG1vaXMuIEVudmlyb24gNzUlIGR1IHRvdGFsIGRlcyBwcsOqdHMgc29udCBhY2NvcmTDqXMgcG91ciB1bmUgZHVyw6llIGRlIDMgYW5zLCB0YW5kaXMgcXVlIHNldWxlbWVudCAyNSUgZGVzIHByw6p0cyBzb250IGFjY29yZMOpcyBwb3VyIHVuZSBkdXLDqWUgZGUgNSBhbnMuCi0gTGEgcGx1cGFydCBkZXMgcHLDqnRzIGFwcHJvdXbDqXMgc29udCBkZSBxdWFsaXTDqSBzdXDDqXJpZXVyZS4gTsOpYW5tb2lucywgdW5lIHBhcnQgbm9uIG7DqWdsaWdlYWJsZSBkZSBwcsOqdHMgc29udCBhY2NvcmTDqXMgc2FucyB2w6lyaWZpY2F0aW9uLgotIFRyw6hzIHBldSBkZSBwcsOqdHMgc29udCBhY2NvcmTDqXMgYXV4IHBlcnNvbm5lcyBheWFudCBkw6lqw6AgZmFpdCBmYWlsbGl0ZSwgZXQgZG9uYyBheWFudCB1bmUgbWF1dmFpc2Ugbm90ZS4KLSBMYSB0csOocyB0csOocyBncmFuZGUgbWFqb3JpdMOpIGRlcyBwcsOqdHMgc29udCBhY2NvcmTDqXMgcG91ciBkZXMgcGVyc29ubmVzIG4nYXlhbnQgZMOpY2xhcsOpZXMgYXVjdW5lIGZhaWxsaXRlIHB1YmxpcXVlLgotIExlIG1vdGlmIGRlIGNvbnNvbGlkYXRpb24gZGUgbGEgZGV0dGUgZXN0IGRlIGxvaW4gbGUgcHJpbmNpcGFsIG1vdGlmIGRlIGRlbWFuZGUgZGUgcHLDqnQgPT4gZW1wcnVudGV1cnMgZW4gc2l0dWF0aW9uIHByw6ljYWlyZS4KCmBgYHtyfQpmaWcxIDwtIHBsb3RfaGlzdG9ncmFtKGRhdGEsIGRhdGEkaG9tZV9vd25lcnNoaXAsICJIb21lIG93bmVyc2hpcCBkaXN0cmlidXRpb24iLCAiSG9tZSBvd25lcnNoaXAiLCAiQ291bnQiKQpmaWcyIDwtIHBsb3RfaGlzdG9ncmFtKGRhdGEsIGRhdGEkdGVybSwgIkxvYW4gdGVybSBkaXN0cmlidXRpb24iLCAiVGVybSIsICJDb3VudCIpCmZpZzMgPC0gcGxvdF9oaXN0b2dyYW0oZGF0YSwgZGF0YSR2ZXJpZmljYXRpb25fc3RhdHVzLCAiVmVyaWZpZWQgc3RhdHVzIHByb3BvcnRpb24iLCAiVmVyaWZpZWQgc3RhdHVzIiwgIkNvdW50IikKZmlnNCA8LSBwbG90X2hpc3RvZ3JhbShkYXRhLCBkYXRhJGdyYWRlLCAiQ3JlZGl0IGdyYWRlIHJlcGFydGl0aW9uIiwgIkdyYWRlIiwgIkNvdW50IikKZmlnNSA8LSBwbG90X2hpc3RvZ3JhbShkYXRhLCBkYXRhJHB1Yl9yZWNfYmFua3J1cHRjaWVzLCAiUHVibGljIGJhbmtydXB0Y3kgZGlzdHJpYnV0aW9uIiwgIlB1YmxpYyBiYW5rcnVwdGN5IiwgIkNvdW50IikKZmlnNiA8LSBwbG90X2hpc3RvZ3JhbShkYXRhLCBkYXRhJHB1cnBvc2UsICJMb2FuIHB1cnBvc2UgcmVwYXJ0aXRpb24iLCAiUHVycG9zZSIsICJDb3VudCIpCgpmaWcgPC0gc3VicGxvdChmaWcxLCBmaWcyLCBmaWczLCBmaWc0LCBmaWc1LCBmaWc2LCBucm93cyA9IDMsIHRpdGxlWSA9IFRSVUUsIHRpdGxlWCA9IFRSVUUsIG1hcmdpbiA9IDAuMSkKZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gIkNldGdvcmljYWwgdmFyaWFibGUiLAogICAgICAgICBwbG90X2JnY29sb3I9IiNGRkZGRkYiLCAKICAgICAgICAgeGF4aXMgPSBsaXN0KAogICAgICAgICAgIHplcm9saW5lY29sb3IgPSAiI2ZmZmYiLCAKICAgICAgICAgICB6ZXJvbGluZXdpZHRoID0gMiwgCiAgICAgICAgICAgZ3JpZGNvbG9yID0gImZmZmYiKSwgCiAgICAgICAgIHlheGlzID0gbGlzdCgKICAgICAgICAgICB6ZXJvbGluZWNvbG9yID0gIiNmZmZmIiwgCiAgICAgICAgICAgemVyb2xpbmV3aWR0aCA9IDIsIAogICAgICAgICAgIGdyaWRjb2xvciA9ICJmZmZmIiksIAogICAgICAgICBzaG93bGVnZW5kID0gRkFMU0UsCiAgICAgICAgIHNob3dsZWdlbmQyID0gRkFMU0UpCgpmaWcKYGBgCgoKIyMjIyBWYXJpYWJsZSA6ICJzdWJfZ3JhZGUiCgpMYSBwbGF0ZWZvcm1lIExlbmRpbmcgQ2x1YiBhdHRyaWJ1ZSB1biBncmFkZSBhaW5zaSBxdSd1biBzb3VzLWdyYWRlIMOgIGNoYXF1ZSBwcsOqdC4KCk9ic2VydmF0aW9uIDoKLSBMZXMgcHLDqnRzIGRlIGNhdMOpZ29yaWUgQSBldCBCIHNvbnQgbGVzIHBsdXMgc8O7cnMuCi0gTGVzIHByw6p0cyBkZSBjYXTDqWdvcmllIEQsIEUsIEYgZXQgRyBzb250IG1vaW5zIHPDu3JzLgotIExlcyBwcsOqdHMgaW1wYXnDqXMgc29udCBlbiBtYWpvcml0w6kgZGUgY2F0w6lnb3JpZSBDIGV0IEQuCi0gTGVzIHByw6p0cyDDoCBwYXJ0aXIgZGUgbGEgY2F0w6lnb3JpZSBFLCBGIGV0IEcgc29udCByaXNxdcOpcyBldCBtb2lucyBub21icmV1eC4KLSBBaW5zaSwgb24gcGV1dCBkaXJlIHF1ZSBsZSBzeXN0w6htZSBkZSBjbGFzc2VtZW50IGRlIExlbmRpbmcgQ2x1YiBmb25jdGlvbm5lLgoKYGBge3J9CmZpZyA8LSBwbG90X2hpc3RvZ3JhbShkYXRhLCBkYXRhJHN1Yl9ncmFkZSwgIkNyZWRpdCBzdWItZ3JhZGUgZGlzdHJpYnV0aW9uIiwgIlN1Yi1ncmFkZSIsICJDb3VudCIpCmZpZwpgYGAKCmBgYHtyfQpmaWcgPC0gcGxvdF9ib3goZGF0YSwgZGF0YSRncmFkZSwgZGF0YSRsb2FuX3N0YXR1cywgIkNyZWRpdCBncmFkZSBkaXN0cmlidXRpb24iLCAiR3JhZGUiLCAiQ291bnQiKQpmaWcKYGBgCgpgYGB7cn0KZmlnIDwtIHBsb3RfYm94KGRhdGEsIGRhdGEkc3ViX2dyYWRlLCBkYXRhJGxvYW5fc3RhdHVzLCAiQ3JlZGl0IHN1Yi1ncmFkZSBkaXN0cmlidXRpb24iLCAiU3ViLWdyYWRlIiwgIkNvdW50IikKZmlnCmBgYAoKCiMjIyMgVmFyaWFibGUgOiBhbm51YWwgaW5jb21lCiAgICAKTGUgcmV2ZW51IGFubnVlbCBhdXRvLWTDqWNsYXLDqSBwYXIgbCdlbXBydW50ZXVyIGxvcnMgZGUgc29uIGluc2NyaXB0aW9uLgoKT2JzZXJ2YXRpb24gOiBsb3JzcXVlIGwnb24gcmVnYXJkZSBsYSByw6lwYXJ0aXRpb24gZGVzIHJldmVudXMgYW5udWVscywgb24gdm9pdCBxdWUgbGEgZGlzdHJpYnV0aW9uIGNvbXBvcnRlIGRlIG5vbWJyZXVzZXMgdmFsZXVycyBleHRyw6ptZXMgY2FyIGxhIGRpc3RyaWJ1dGlvbiBlc3QgbGVwdG9rdXJ0aWMgKGt1cnRvc2lzIMOpZ2FsZSDDoCA5NzAuMykgZXQgdHLDqHMgdHLDqHMgw6l0YWzDqWUgdmVycyBsYSBkcm9pdGUgKGxhIHByZXV2ZSBhdmVjIHVuZSBza2V3bmVzcyBkZSAxOC43KS4gTm91cyBvYnNlcnZvbnMgdW4gw6ljYXJ0IGRlIDEwIDAwMCQgZW50cmUgbGEgbcOpZGlhbmUgZXQgbGEgbW95ZW5uZS4KTGVzIG1veWVubmVzIGV0IG3DqWRpYW5lcyBkZXMgZGlmZsOpcmVudGVzIGNsYXNzZXMgc29udCByZWxhdGl2ZW1lbnQgInByb2NoZXMiLiBFbiByZXZhbmNoZSBub3VzIHBvdXZvbnMgdm9pciBxdWUgcG91ciBsZXMgZW1wcnVudGV1cnMgZW4gcMOpcmlvZGUgZGUgZ3LDomNlLCBsZXVyIG1veWVubmUgZXQgbcOpZGlhbmUgc29udCBhbm9ybWFsZW1lbnQgw6lsZXbDqWVzIGVuIHJhaXNvbiBkdSBmYWlibGUgbm9tYnJlIGQnb2JzZXJ2YXRpb25zIGRlIGNldHRlIGNsYXNzZSBkYW5zIG5vdHJlIMOpY2hhbnRpbGxvbi4KCmBgYHtyfQpmaWcgPC0gcGxvdF9zdWJwbG90KGRhdGEsIGRhdGEkYW5udWFsX2luYywgZGF0YSRsb2FuX3N0YXR1cywgIkFubnVhbCBpbmNvbWUgdmFyaWFibGUiLCAiQW5udWFsIGluY29tZSIsICJDb3VudCIpCmZpZwpgYGAKCmBgYHtyfQpzdGF0X2Rlc2MgPC0gZGVzY3JpcHRpdmVfc3RhdGlzdGljMShkYXRhLCBkYXRhJGFubnVhbF9pbmMpCnN0YXRfZGVzYwpgYGAKCmBgYHtyfQpkYXRhICU+JQogIGdyb3VwX2J5KGxvYW5fc3RhdHVzKSAlPiUKICBzdW1tYXJpc2UoTWluaW11bSA9IHJvdW5kKG1pbihhbm51YWxfaW5jKSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgIE1heGltdW0gPSByb3VuZChtYXgoYW5udWFsX2luYyksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBNb3llbm5lID0gcm91bmQobWVhbihhbm51YWxfaW5jKSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgIE1lZGlhbmUgPSByb3VuZChtZWRpYW4oYW5udWFsX2luYyksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBWYXJpYW5jZSA9IHJvdW5kKHZhcihhbm51YWxfaW5jKSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgIEVjYXJ0X3R5cGUgPSByb3VuZChzZChhbm51YWxfaW5jKSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgIEt1cnRvc2lzID0gcm91bmQoa3VydG9zaXMoYW5udWFsX2luYyksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBTa2V3bmVzcyA9IHJvdW5kKHNrZXduZXNzKGFubnVhbF9pbmMpLCBkaWdpdHMgPSA0KSkKYGBgCgoKUXVpZCBkdSByZXZlbnUgYW5udWVsIGF1dG8tZMOpY2xhcsOpIHNlbG9uIGxlIGdyYWRlIGRlIGNyw6lkaXQgTGVuZGluZyBDbHViIGRlIGwnZW1wcnVudGV1ciBldCBsZSBzdGF0dXQgZGUgbCdlbXBydW50ZXVyID8KTm91cyByZW1hcnF1b25zIHF1ZSBsZXMgZ3JhZGVzIGxlcyBtaWV1eCBub3TDqXMgc29udCBjZXV4IHF1aSBhZmZpY2hlbnQgbGVzIHBsdXMgZm9ydHMgcmV2ZW51cyBhbm51ZWxzLiBMZXMgcmV2ZW51cyBkw6ljcm9pc3NlbnQgYXZlYyBsZXMgZ3JhZGVzLgoKYGBge3J9CmdncGxvdF9ib3goZGF0YTIsIGRhdGEyJGdyYWRlLCBkYXRhMiRhbm51YWxfaW5jLCBkYXRhMiRkZWZhdWx0LCAiQW5udWFsIGluY29tZSBieSBHcmFkZSIsICJHcmFkZSIsICJBbm51YWwgaW5jb21lIFxuIikKYGBgCgpRdWlkIGR1IHJldmVudSBhbm51ZWwgYXV0by1kw6ljbGFyw6kgc2Vsb24gbGUgZ3JhZGUgZGUgY3LDqWRpdCBMZW5kaW5nIENsdWIgZGUgbCdlbXBydW50ZXVyIGV0IGwnw6ljaMOpYW5jZSBkZSBjcsOpZGl0ID8KTm91cyBhdm9ucyBsJ2ltcHJlc3Npb24gcXVlIGxlcyBlbXBydW50ZXVycyBhdmVjIGxlcyBwbHVzIGhhdXRzIHJldmVudXMgZXQgbGVzIG1laWxsZXVycyBncmFkZXMgZW1wcnVudGVudCDDoCBjb3VydC10ZXJtZSB0YW5kaXMgcXVlIGNldXggYXZlYyBsZXMgcGx1cyBmYWlibGVzIHJldmVudXMgYW5udWVscyBldCBsZXMgcGx1cyBtYXV2YWlzIGdyYWRlcyBlbXBydW50ZW50IGRhdmFudGFnZSDDoCBsb25nLXRlcm1lLgoKYGBge3J9CmRhdGEyICU+JQogIGdncGxvdChhZXMoZ3JhZGUsIGFubnVhbF9pbmMpKSArCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAid2hpdGUiLCBjb2xvdXIgPSAiZGFya2JsdWUiLCAKICAgICAgICAgICAgICAgb3V0bGllci5jb2xvdXIgPSAicmVkIiwgb3V0bGllci5zaGFwZSA9IDEpICsKICBnaXZlX2NvdW50ICsKICBnaXZlX21lYW4gKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGxhYnModGl0bGU9IkFubnVhbCBpbmNvbWUgYnkgR3JhZGUiLCB4ID0gIkdyYWRlIiwgeSA9ICJBbm51YWwgaW5jb21lIFxuIikgKwogIGZhY2V0X3dyYXAofiB0ZXJtKQpgYGAKCgojIyMjIFZhcmlhYmxlIDogYWRkciBzdGF0ZQoKTCfDqXRhdCBmb3VybmkgcGFyIGwnZW1wcnVudGV1ciBkYW5zIGxhIGRlbWFuZGUgZGUgcHLDqnQuCgpPYnNlcnZhdGlvbiA6IGxlcyBwcsOqdHMgbGVzIHBsdXMgaW1wb3J0YW50cyBwcm92aWVubmVudCBkZSBsJ0V0YXQgZGUgbGEgQ2FsaWZvcm5pZSwgc3VpdmkgcGFyIGwnRXRhdCBkZSBOZXctWW9yaywgZGUgbGEgRmxvcmlkZSBldCBkdSBUZXhhcy4gSWwgZmF1dCBzJ3kgYXR0ZW5kcmUgY2FyIGNlIHNvbnQgw6lnYWxlbWVudCBsZXMgdHJvaXMgw4l0YXRzIGFtw6lyaWNhaW5zIGxlcyBwbHVzIHBldXBsw6lzLiBMZXMgw4l0YXRzIGF5YW50IHVuIHRhdXggZGUgZMOpZmFpbGxhbmNlIHBsdXMgw6lsZXbDqSBvbnQgdW4gbm9tYnJlIGRlIHByw6p0cyB0csOocyBmYWlibGUuIExlIHBvdXJjZW50YWdlIG4nZXN0IGRvbmMgUEFTIHNpZ25pZmljYXRpZiBldCBkb2l0IMOqdHJlIGlnbm9yw6kuIEdsb2JhbGVtZW50LCBjZXR0ZSB2YXJpYWJsZSBuJ2FmZmVjdGUgcGFzIGxhIHByb3BlbnNpb24gYXUgZMOpZmF1dC4KCmBgYHtyfQpmaWcgPC0gcGxvdF9oaXN0b2dyYW0oZGF0YSwgZGF0YSRhZGRyX3N0YXRlLCAiQm9ycm93ZXIncyBzdGF0ZSIsICJVUyBzdGF0ZSIsICJDb3VudCIpCmZpZwpgYGAKCkVzdC1jZSBxdWUgY2VydGFpbnMgw6l0YXRzIGRlcyBFdGF0cy1VbmlzIGFmZmljaGVudCBkZXMgdGF1eCBkZSBkw6lmYXV0IG5ldHRlbWVudCBzdXDDqXJpZXVycyDDoCBkJ2F1dHJlcyDDqXRhdHMgPwotIE9rbGFob21hIDogMzEuMzUlCi0gTmVicmFza2EgOiAzMC40MyUKTGVzIMOpdGF0cyBkZSBsJ09rbGFob21hIGV0IGR1IE5lYnJhc2thIHNvbnQgY2V1eCBhZmZpY2hhbnQgdW4gdGF1eCBkZSBkw6lmYXV0IHN1cMOpcmlldXIgw6AgMzAlLgoKLSBPcmVnb24gOiAxMy4yNSUKLSBEYWtvdGEgZHUgU3VkIDogMTIuNTAlCsOAIGwnaW52ZXJzZSwgbGVzIMOpdGF0cyBkZSBsJ09yZWdvbiBldCBkdSBEYWtvdGEgZHUgU3VkIHNvbnQgY2V1eCBhZmZpY2hhbnQgbGVzIHBsdXMgZmFpYmxlcyB0YXV4IGRlIGTDqWZhdXQuCgpMZXMgw6l0YXRzIHBldXZlbnQgZG9uYyBhdm9pciB1biBwb3V2b2lyIGluZm9ybWF0aWYgZGFucyBsYSBzYW50w6kgZmluYW5jacOocmUgZGVzIGVtcHJ1bnRldXJzLiBGb3Jjw6ltZW50IGRhbnMgbGVzIMOpdGF0cyBvw7kgbGUgdGF1eCBkJ2VtcGxvaXMsIGxlIHRhdXggZCd1cmJhbmlzYXRpb24gZXN0IGZvcnQsIGxlIHRhdXggZGUgZMOpZmF1dCBlc3QgcGx1cyBmYWlibGUuCgpgYGB7cn0KZGVmYXVsdF9yYXRlX3N0YXRlIDwtIAogIGRhdGEyICU+JQogIHNlbGVjdChkZWZhdWx0LCBhZGRyX3N0YXRlKSAlPiUKICBncm91cF9ieShhZGRyX3N0YXRlKSAlPiUKICBzdW1tYXJpc2UoZGVmYXVsdF9yYXRlID0gc3VtKGRlZmF1bHQsIG5hLnJtID0gVFJVRSkgLyBuKCkpCgpkZWZhdWx0X3JhdGVfc3RhdGUKYGBgCgoKIyMjIyBWYXJpYWJsZSA6ICJkZWxpbnFfMnlycyIKCkxlIG5vbWJyZSBkJ2luY2lkZW5jZXMgZCdpbXBhecOpcyBkZSBwbHVzIGRlIDMwIGpvdXJzIGRhbnMgbGUgZG9zc2llciBkZSBjcsOpZGl0IGRlIGwnZW1wcnVudGV1ciBhdSBjb3VycyBkZXMgZGV1eCBkZXJuacOocmVzIGFubsOpZXMuCgpPYnNlcnZhdGlvbiA6IGxlcyByaXNxdWVzIGRlIGTDqWZhaWxsYW5jZSBzb250IHBsdXMgw6lsZXbDqXMgc2kgY2V0dGUgdmFyaWFibGUgZXN0IHN1cMOpcmlldXJlIMOgIDEuIEhldXJldXNlbWVudCBzYSBtw6lkaWFuZSBlc3Qgw6lnYWxlIMOgIDAuIE5vdXMgcG91dm9ucyB2b2lyIHF1J2lsIG4neSBhIHBhcyB1bmUgdHLDqHMgZ3JhbmRlIGRpZmbDqXJlbmNlIGVudHJlIGxlcyBjbGFzc2VzLiBQYXIgZXhlbXBsZSwgbGEgbW95ZW5uZSBkZXMgZW1wcnVudGV1cnMgZW4gZMOpZmF1dCBlc3QgZGUgMC4zNiBub21icmVzIGQnaW5jaWRlbnQgc3VyIGxlcyAyIGRlcm5pw6hyZXMgYW5uw6llcyBjb250cmUgMC4zMyBub21icmVzIGQnaW5jaWRlbnQgcG91ciBsZXMgZW1wcnVudGV1cnMgYXlhbnQgdG90YWxlbWVudCByZW1ib3Vyc8OpcyBsZXVyIGVtcHJ1bnQuCgpgYGB7cn0KZmlnIDwtIHBsb3RfaGlzdG9ncmFtKGRhdGEsIGRhdGEkZGVsaW5xXzJ5cnMsICIyIHllYXJzIGRlbGlucSBkaXN0cmlidXRpb24iLCAiMiB5ZWFycyBkZWxpbnEiLCAiQ291bnQiKQpmaWcKYGBgCgpgYGB7cn0Kc3RhdF9kZXNjIDwtIGRlc2NyaXB0aXZlX3N0YXRpc3RpYzEoZGF0YSwgZGF0YSRkZWxpbnFfMnlycykKc3RhdF9kZXNjCmBgYAoKYGBge3J9CmRhdGEgJT4lCiAgZ3JvdXBfYnkobG9hbl9zdGF0dXMpICU+JQogIHN1bW1hcmlzZShNaW5pbXVtID0gcm91bmQobWluKGRlbGlucV8yeXJzKSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgIE1heGltdW0gPSByb3VuZChtYXgoZGVsaW5xXzJ5cnMpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgTW95ZW5uZSA9IHJvdW5kKG1lYW4oZGVsaW5xXzJ5cnMpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgTWVkaWFuZSA9IHJvdW5kKG1lZGlhbihkZWxpbnFfMnlycyksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBWYXJpYW5jZSA9IHJvdW5kKHZhcihkZWxpbnFfMnlycyksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBFY2FydF90eXBlID0gcm91bmQoc2QoZGVsaW5xXzJ5cnMpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgS3VydG9zaXMgPSByb3VuZChrdXJ0b3NpcyhkZWxpbnFfMnlycyksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBTa2V3bmVzcyA9IHJvdW5kKHNrZXduZXNzKGRlbGlucV8yeXJzKSwgZGlnaXRzID0gNCkpCmBgYAoKUXVpZCBkdSBub21icmUgZGUgZMOpbGlucXVhbmNlIHN1ciBsZXMgMiBkZXJuacOocmVzIGFubsOpZXMgc2Vsb24gbGUgZ3JhZGUgZGUgY3LDqWRpdCBMZW5kaW5nIENsdWIgZGUgbCdlbXBydW50ZXVyIGV0IGxlIHN0YXR1dCBkZSBsJ2VtcHJ1bnRldXIgPwpBdmVjIGwnYWlkZSBkZXMgb3V0bGllcnMsIG5vdXMgcG91dm9ucyBub3RlciBxdWUgbGUgbm9tYnJlIGRlIGTDqWxpbnF1YW5jZSBzdXIgbGVzIDIgZGVybmnDqHJlcyBhbm7DqWVzIGEgdGVuZGFuY2Ugw6AgYXVnbWVudGVyIHBvdXIgbGVzIGVtcHJ1bnRldXJzIGVuIGTDqWZhdXQgZGUgcGFpZW1lbnQgZXQgZCdhdXRhbnQgcGx1cyBwb3VyIGxlcyBncmFkZXMgZGUgY3LDqWRpdCBsZXMgcGx1cyBtYXV2YWlzIHBvdXIgbGVzIGNsaWVudHMgZW4gZMOpZmF1dC4KCmBgYHtyfQpnZ3Bsb3RfYm94KGRhdGEyLCBkYXRhMiRncmFkZSwgZGF0YTIkZGVsaW5xXzJ5cnMsIGRhdGEyJGRlZmF1bHQsICJOdW1iZXIgb2YgMnkgZGVsaW5xIGJ5IEdyYWRlIiwgIkdyYWRlIiwgIk51bWJlciBvZiAyeSBkZWxpbnEgXG4iKQpgYGAKClF1aWQgZHUgbm9tYnJlIGRlIGTDqWxpbnF1YW5jZSBzdXIgbGVzIDIgZGVybmnDqHJlcyBhbm7DqWVzIHNlbG9uIGxlIGdyYWRlIGRlIGNyw6lkaXQgTGVuZGluZyBDbHViIGRlIGwnZW1wcnVudGV1ciBldCBsJ8OpY2jDqWFuY2UgZGUgY3LDqWRpdCA/CkF2ZWMgbCdhaWRlIGRlcyBvdXRsaWVycywgbm91cyBwb3V2b25zIG5vdGVyIHF1ZSBsZSBub21icmUgZGUgZMOpbGlucXVhbmNlIHN1ciBsZXMgMiBkZXJuacOocmVzIGFubsOpZXMgYSB0ZW5kYW5jZSDDoCBhdWdtZW50ZXIgcG91ciBsZXMgZW1wcnVudHMgZW4gZMOpZmF1dCBkZSBwYWllbWVudCBldCBkJ2F1dGFudCBwbHVzIHBvdXIgbGVzIGdyYWRlcyBkZSBjcsOpZGl0IGxlcyBwbHVzIG1hdXZhaXMgcG91ciBsZXMgZW1wcnVudHMgZGUgbG9uZy10ZXJtZS4KCmBgYHtyfQpkYXRhMiAlPiUKICBnZ3Bsb3QoYWVzKGdyYWRlLCBkZWxpbnFfMnlycykpICsKICBnZW9tX2JveHBsb3QoZmlsbCA9ICJ3aGl0ZSIsIGNvbG91ciA9ICJkYXJrYmx1ZSIsIAogICAgICAgICAgICAgICBvdXRsaWVyLmNvbG91ciA9ICJyZWQiLCBvdXRsaWVyLnNoYXBlID0gMSkgKwogIGdpdmVfY291bnQgKwogIGdpdmVfbWVhbiArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgbGFicyh0aXRsZT0iTnVtYmVyIG9mIDJ5IGRlbGlucSBieSBHcmFkZSIsIHggPSAiR3JhZGUiLCB5ID0gIk51bWJlciBvZiAyeSBkZWxpbnEgXG4iKSsKICBmYWNldF93cmFwKH4gdGVybSkKYGBgCgoKIyMjIyBWYXJpYWJsZSA6ICJpbnFfbGFzdF82bXRocyIKCk5vbWJyZSBkZSBkZW1hbmRlcyBkZSByZW5zZWlnbmVtZW50cyBhdSBjb3VycyBkZXMgNiBkZXJuaWVycyBtb2lzICjDoCBsJ2V4Y2x1c2lvbiBkZXMgZGVtYW5kZXMgZGUgcmVuc2VpZ25lbWVudHMgc3VyIGxlcyB2w6loaWN1bGVzIGV0IGxlcyBwcsOqdHMgaHlwb3Row6ljYWlyZXMpLgoKT2JzZXJ2YXRpb24gOiBsZXMgZW1wcnVudGV1cnMgcXVpIHBvc3PDqGRlbnQgcGx1cyBkZSBkZXV4IGRlbWFuZGVzIGRlIHJlbnNlaWduZW1lbnRzIG9udCB1biB0YXV4IGRlIGTDqWZhaWxsYW5jZSBjb21tZW7Dp2FudCDDoCDDqnRyZSBzaWduaWZpY2F0aXZlbWVudCBwbHVzIMOpbGV2w6kuCgpgYGB7cn0KZmlnIDwtIHBsb3RfaGlzdG9ncmFtKGRhdGEsIGRhdGEkaW5xX2xhc3RfNm10aHMsICJSZXF1ZXN0IGZvciBpbmZvcm1hdGlvbiBkaXN0cmlidXRpb24iLCAiUmVxdWVzdCBmb3IgaW5mb3JtYXRpb24iLCAiQ291bnQiKQpmaWcKYGBgCgpgYGB7cn0Kc3RhdF9kZXNjIDwtIGRlc2NyaXB0aXZlX3N0YXRpc3RpYzEoZGF0YSwgZGF0YSRpbnFfbGFzdF82bXRocykKc3RhdF9kZXNjCmBgYAoKYGBge3J9CmRhdGEgJT4lCiAgZ3JvdXBfYnkobG9hbl9zdGF0dXMpICU+JQogIHN1bW1hcmlzZShNaW5pbXVtID0gcm91bmQobWluKGlucV9sYXN0XzZtdGhzKSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgIE1heGltdW0gPSByb3VuZChtYXgoaW5xX2xhc3RfNm10aHMpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgTW95ZW5uZSA9IHJvdW5kKG1lYW4oaW5xX2xhc3RfNm10aHMpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgTWVkaWFuZSA9IHJvdW5kKG1lZGlhbihpbnFfbGFzdF82bXRocyksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBWYXJpYW5jZSA9IHJvdW5kKHZhcihpbnFfbGFzdF82bXRocyksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBFY2FydF90eXBlID0gcm91bmQoc2QoaW5xX2xhc3RfNm10aHMpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgS3VydG9zaXMgPSByb3VuZChrdXJ0b3NpcyhpbnFfbGFzdF82bXRocyksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBTa2V3bmVzcyA9IHJvdW5kKHNrZXduZXNzKGlucV9sYXN0XzZtdGhzKSwgZGlnaXRzID0gNCkpCmBgYAoKUXVpZCBkdSBub21icmUgZGUgZGVtYW5kZXMgZGUgcmVuc2VpZ25lbWVudHMgYXUgY291cnMgZGVzIDYgZGVybmllcnMgbW9pcyBzZWxvbiBsZSBncmFkZSBkZSBjcsOpZGl0IExlbmRpbmcgQ2x1YiBkZSBsJ2VtcHJ1bnRldXIgZXQgbGUgc3RhdHV0IGRlIGwnZW1wcnVudGV1ciA/CkVuIG1veWVubmUsIHBsdXMgbGUgZ3JhZGUgZGUgY3LDqWRpdCBzZSBkw6l0w6lyaW9yZSBldCBwbHVzIGxlIG5vbWJyZSBkZSBkZW1hbmRlcyBkZSByZW5zZWlnbmVtZW50cyBhdSBjb3VycyBkZXMgNiBkZXJuaWVycyBtb2lzIHRlbmQgw6AgYXVnbWVudGVyIHBvdXIgbGVzIGRldXggY2xhc3Nlcy4KCmBgYHtyfQpnZ3Bsb3RfYm94KGRhdGEyLCBkYXRhMiRncmFkZSwgZGF0YTIkaW5xX2xhc3RfNm10aHMsIGRhdGEyJGRlZmF1bHQsICJOdW1iZXIgb2YgNm0gaW5xIGJ5IEdyYWRlIiwgIkdyYWRlIiwgIk51bWJlciBvZiA2bSBpbnEgXG4iKQpgYGAKClF1aWQgZHUgbm9tYnJlIGRlIGRlbWFuZGVzIGRlIHJlbnNlaWduZW1lbnRzIGF1IGNvdXJzIGRlcyA2IGRlcm5pZXJzIG1vaXMgc2Vsb24gbGUgZ3JhZGUgZGUgY3LDqWRpdCBMZW5kaW5nIENsdWIgZGUgbCdlbXBydW50ZXVyIGV0IGwnw6ljaMOpYW5jZSBkZSBjcsOpZGl0ID8KRW4gbW95ZW5uZSwgcGx1cyBsZSBncmFkZSBkZSBjcsOpZGl0IHNlIGTDqXTDqXJpb3JlIGV0IHBsdXMgbGUgbm9tYnJlIGRlIGRlbWFuZGVzIGRlIHJlbnNlaWduZW1lbnRzIGF1IGNvdXJzIGRlcyA2IGRlcm5pZXJzIG1vaXMgdGVuZCDDoCBhdWdtZW50ZXIgcG91ciBsZXMgZGV1eCBob3Jpem9ucyBkJ2VtcHJ1bnQuIE5vdXMgcG91dm9ucyB0b3V0IGRlIG3Dqm1lIHJlbWFycXVlciBxdWUgbGEgbW95ZW5uZSBkdSBub21icmUgZGUgZGVtYW5kZXMgZGUgcmVuc2VpZ25lbWVudHMgYXUgY291cnMgZGVzIDYgZGVybmllcnMgbW9pcyBhIHRlbmRhbmNlIMOgIGF1Z21lbnRlciBwbHVzIGxlbnRlbWVudCBwb3VyIGxlcyBlbXBydW50cyBzdXIgNjAgbW9pcyBxdWUgc3VyIDM2LiBFbiBnw6luw6lyYWwsIGMnZXN0IMOgIHBhcnRpciBkdSBncmFkZSBEIHF1ZSBsYSBtb3llbm5lIGF1Z21lbnRlIGFzc2V6IGZvcnRlbWVudCBwYXIgcmFwcG9ydCBhdSBncmFkZSBwcsOpY8OpZGVudC4KCmBgYHtyfQpkYXRhMiAlPiUKICBnZ3Bsb3QoYWVzKGdyYWRlLCBpbnFfbGFzdF82bXRocykpICsKICBnZW9tX2JveHBsb3QoZmlsbCA9ICJ3aGl0ZSIsIGNvbG91ciA9ICJkYXJrYmx1ZSIsIAogICAgICAgICAgICAgICBvdXRsaWVyLmNvbG91ciA9ICJyZWQiLCBvdXRsaWVyLnNoYXBlID0gMSkgKwogIGdpdmVfY291bnQgKwogIGdpdmVfbWVhbiArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgbGFicyh0aXRsZT0iTnVtYmVyIG9mIDZtIGlucSBieSBHcmFkZSIsIHggPSAiR3JhZGUiLCB5ID0gIk51bWJlciBvZiA2bSBpbnEgXG4iKSsKICBmYWNldF93cmFwKH4gdGVybSkKYGBgCgoKIyMjIyBWYXJpYWJsZSA6ICJvcGVuX2FjYyIKCklsIHMnYWdpdCBkdSBub21icmUgZGUgbGlnbmVzIGRlIGNyw6lkaXQgb3V2ZXJ0ZXMgZGFucyBsZSBkb3NzaWVyIGRlIGNyw6lkaXQgZGUgbCdlbXBydW50ZXVyLgoKT2JzZXJ2YXRpb24gOiBpbCBuJ3kgYSBwYXMgZGUgZGlmZsOpcmVuY2Ugc2lnbmlmaWNhdGl2ZSBlbnRyZSBsZXMgbGlnbmVzIGRlIGNyw6lkaXQgZGVzIHByw6p0cyBlbiBkw6lmYXV0LCBvdSBhdXRyZSwgZXQgY2VsbGVzIGRlcyBwcsOqdHMgZW50acOocmVtZW50IHJlbWJvdXJzw6lzIChtb3llbm5lIGV0IG3DqWRpYW5lIHRyw6hzIHByb2NoZXMpLiBOb3RvbnMgcXUnaWwgeSBhIGRlIG5vbWJyZXV4IG91dGxpZXJzIMOgIHRyYWl0ZXIuCgpgYGB7cn0KZmlnIDwtIHBsb3Rfc3VicGxvdChkYXRhLCBkYXRhJG9wZW5fYWNjLCBkYXRhJGxvYW5fc3RhdHVzLCAiT3BlbiBjcmVkaXQgbGluZSB2YXJpYWJsZSIsICJPcGVuIGNyZWRpdCBsaW5lIiwgIkNvdW50IikKZmlnCmBgYAoKYGBge3J9CnN0YXRfZGVzYyA8LSBkZXNjcmlwdGl2ZV9zdGF0aXN0aWMxKGRhdGEsIGRhdGEkb3Blbl9hY2MpCnN0YXRfZGVzYwpgYGAKCmBgYHtyfQpkYXRhICU+JQogIGdyb3VwX2J5KGxvYW5fc3RhdHVzKSAlPiUKICBzdW1tYXJpc2UoTWluaW11bSA9IHJvdW5kKG1pbihvcGVuX2FjYyksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBNYXhpbXVtID0gcm91bmQobWF4KG9wZW5fYWNjKSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgIE1veWVubmUgPSByb3VuZChtZWFuKG9wZW5fYWNjKSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgIE1lZGlhbmUgPSByb3VuZChtZWRpYW4ob3Blbl9hY2MpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgVmFyaWFuY2UgPSByb3VuZCh2YXIob3Blbl9hY2MpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgRWNhcnRfdHlwZSA9IHJvdW5kKHNkKG9wZW5fYWNjKSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgIEt1cnRvc2lzID0gcm91bmQoa3VydG9zaXMob3Blbl9hY2MpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgU2tld25lc3MgPSByb3VuZChza2V3bmVzcyhvcGVuX2FjYyksIGRpZ2l0cyA9IDQpKQpgYGAKCgojIyMjIFZhcmlhYmxlIDogInRvdGFsX2FjYyIKCklsIHMnYWdpdCBkdSBub21icmUgdG90YWwgZGUgbGlnbmVzIGRlIGNyw6lkaXQgZmlndXJhbnQgYWN0dWVsbGVtZW50IGRhbnMgbGUgZG9zc2llciBkZSBjcsOpZGl0IGRlIGwnZW1wcnVudGV1ci4KCk9ic2VydmF0aW9uIDogaWwgbid5IGEgcGFzIGRlIGRpZmbDqXJlbmNlIHNpZ25pZmljYXRpdmUgZW50cmUgbGVzIGxpZ25lcyBkZSBjcsOpZGl0IGRlcyBwcsOqdHMgZW4gZMOpZmF1dCwgb3UgYXV0cmUsIGV0IGNlbGxlcyBkZXMgcHLDqnRzIGVudGnDqHJlbWVudCByZW1ib3Vyc8OpcyAobW95ZW5uZSBldCBtw6lkaWFuZSB0csOocyBwcm9jaGVzKS4gTm90b25zIHF1J2lsIHkgYSBkZSBub21icmV1eCBvdXRsaWVycyDDoCB0cmFpdGVyLgoKYGBge3J9CmZpZyA8LSBwbG90X3N1YnBsb3QoZGF0YSwgZGF0YSR0b3RhbF9hY2MsIGRhdGEkbG9hbl9zdGF0dXMsICJUb3RsYSBjcmVkaXQgbGluZSB2YXJpYWJsZSIsICJUb3RsYSBjcmVkaXQgbGluZSIsICJDb3VudCIpCmZpZwpgYGAKCmBgYHtyfQpzdGF0X2Rlc2MgPC0gZGVzY3JpcHRpdmVfc3RhdGlzdGljMShkYXRhLCBkYXRhJHRvdGFsX2FjYykKc3RhdF9kZXNjCmBgYAoKYGBge3J9CmRhdGEgJT4lCiAgZ3JvdXBfYnkobG9hbl9zdGF0dXMpICU+JQogIHN1bW1hcmlzZShNaW5pbXVtID0gcm91bmQobWluKHRvdGFsX2FjYyksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBNYXhpbXVtID0gcm91bmQobWF4KHRvdGFsX2FjYyksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBNb3llbm5lID0gcm91bmQobWVhbih0b3RhbF9hY2MpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgTWVkaWFuZSA9IHJvdW5kKG1lZGlhbih0b3RhbF9hY2MpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgVmFyaWFuY2UgPSByb3VuZCh2YXIodG90YWxfYWNjKSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgIEVjYXJ0X3R5cGUgPSByb3VuZChzZCh0b3RhbF9hY2MpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgS3VydG9zaXMgPSByb3VuZChrdXJ0b3Npcyh0b3RhbF9hY2MpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgU2tld25lc3MgPSByb3VuZChza2V3bmVzcyh0b3RhbF9hY2MpLCBkaWdpdHMgPSA0KSkKYGBgCgpRdWlkIGR1IG5vbWJyZSBkZSBsaWduZXMgZGUgY3LDqWRpdCBvdXZlcnRlcyBzZWxvbiBsZSBncmFkZSBkZSBjcsOpZGl0IExlbmRpbmcgQ2x1YiBkZSBsJ2VtcHJ1bnRldXIgZXQgbGUgc3RhdHV0IGRlIGwnZW1wcnVudGV1ciA/CkljaSwgbm91cyBhdXJpb25zIHB1IG5vdXMgYXR0ZW5kcmUgYXUgZmFpdCBxdWUgbGVzIGVtcHJ1bnRldXJzIGVuIGTDqWZhdXQgYWllbnQgZGF2YW50YWdlIGRlIGxpZ25lcyBkZSBjcsOpZGl0IG91dmVydGVzLiBFbiByw6lhbGl0w6kgY2VsYSBuJ2VzdCBwYXMgdnJhaW1lbnQgdsOpcmlmacOpLiBPbiByZW1hcnF1ZSBtw6ptZSBxdSdpbCBuJ3kgYSBwYXMgdnJhaW1lbnQgZGUgZGlmZsOpcmVuY2UgZW50cmUgbGVzIGdyYWRlcyBkZSBjcsOpZGl0LiBQYXIgZXhlbXBsZSBwb3VyIGxlcyBjbGllbnRzIHNhaW5zLCBsZSBncmFkZSBBIHBvc3PDqGRlLCBlbiBtb3llbm5lLCBlbnZpcm9uIDI1IGxpZ25lcyBkZSBjcsOpZGl0IG91dmVydGVzIGF1IHRvdGFsIHN1ciB0b3V0ZXMgbGV1ciB2aWUgdGFuZGlzIHF1ZSBsZSBncmFkZSBHIHVuIHBldSBtb2lucy4gCkF0dGVudGlvbiBjZXR0ZSBpbnRlcnByw6l0YXRpb24gZXN0IMOgIHByZW5kcmUgYXZlYyBkZSBncm9zc2VzIHBpbmNldHRlcyDDqXRhbnQgZG9ubsOpIHF1ZSBub3VzIG4nYXZvbnMgcGFzIGF1dGFudCBkJ29ic2VydmF0aW9ucyBkYW5zIGNlcyBkZXV4IGdyYWRlcyBkZSBjcsOpZGl0IGV0IHF1J2lscyBuZSBzb250IGRvbmMgcGFzIHRyw6hzIGNvbXBhcmFibGVzLiBMYSBsw6lnw6hyZSBkw6ljcm9pc3NhbmNlIGR1IG5vbWJyZSBkZSBsaWduZXMgZGUgY3LDqWRpdCBvdXZlcnRlcyBwYXIgZW1wcnVudGV1ciBzJ2V4cGxpcXVlIHBhciBsYSBkw6ljcm9pc3NhbmNlIGR1IG5vbWJyZSBkJ29ic2VydmF0aW9ucyBkYW5zIGxlcyBncmFkZXMgbGVzIHBsdXMgbWF1dmFpcy4KCmBgYHtyfQpnZ3Bsb3RfYm94KGRhdGEyLCBkYXRhMiRncmFkZSwgZGF0YTIkdG90YWxfYWNjLCBkYXRhMiRkZWZhdWx0LCAiTnVtYmVyIG9mIG9wZW5lZCBjcmVkaXQgbGluZSBieSBHcmFkZSIsICJHcmFkZSIsICJOdW1iZXIgb2Ygb3BlbmVkIGNyZWRpdCBsaW5lIFxuIikKYGBgCgoKIyMjIyBWYXJpYWJsZSAgOiAibGFzdF9weW1udF9hbW50IgoKRGVybmllciBtb250YW50IHRvdGFsIGR1IHBhaWVtZW50IHJlw6d1LgoKT2JzZXJ2YXRpb24gOiBsZSBtb250YW50IGR1IGRlcm5pZXIgdmVyc2VtZW50IHJlw6d1IGVzdCBuZXR0ZW1lbnQgaW5mw6lyaWV1ciBwb3VyIGxlcyBwcsOqdHMgaW1wYXnDqXMgcGFyIHJhcHBvcnQgYXV4IHByw6p0cyBlbnRpw6hyZW1lbnQgcmVtYm91cnPDqXMuIElsIHkgYSB1biDDqWNhcnQgYXNzZXogZm9ydCBlbnRyZSBsZXMgcHLDqnRzIHRvdGFsZW1lbnQgcmVtYm91cnPDqXMgZXQgbGVzIGF1dHJlcyA6IGxhIG1veWVubmUgZXQgbcOpZGlhbmUgc29udCByZWxhdGl2ZW1lbnQgYmllbiBwbHVzIMOpbGV2w6llcyAobW95ZW5uZSBmdWxseSBwYWlkIDU4MzcuODQgY29udHJlIG1veWVubmUgY2hhcmdlZCBvZmYgNDU1LjIxKS4KRGUgbm9tYnJldXggb3V0bGllcnMgcG91ciBsYSBtb2RhbGl0w6kgZGVzIHByw6p0cyB0b3RhbGVtZW50IHJlbWJvdXJzw6lzLgoKYGBge3J9CmZpZyA8LSBwbG90X3N1YnBsb3QoZGF0YSwgZGF0YSRsYXN0X3B5bW50X2FtbnQsIGRhdGEkbG9hbl9zdGF0dXMsICJUb3RhbCBwYWlkIGFtb3VudCB2YXJpYWJsZSIsICJUb3RhbCBwYWlkIGFtb3VudCIsICJDb3VudCIpCmZpZwpgYGAKCmBgYHtyfQpzdGF0X2Rlc2MgPC0gZGVzY3JpcHRpdmVfc3RhdGlzdGljMShkYXRhLCBkYXRhJGxhc3RfcHltbnRfYW1udCkKc3RhdF9kZXNjCmBgYAoKYGBge3J9CmRhdGEgJT4lCiAgZ3JvdXBfYnkobG9hbl9zdGF0dXMpICU+JQogIHN1bW1hcmlzZShNaW5pbXVtID0gcm91bmQobWluKGxhc3RfcHltbnRfYW1udCksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBNYXhpbXVtID0gcm91bmQobWF4KGxhc3RfcHltbnRfYW1udCksIGRpZ2l0cyA9IDQpLAogICAgICAgICAgICBNb3llbm5lID0gcm91bmQobWVhbihsYXN0X3B5bW50X2FtbnQpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgTWVkaWFuZSA9IHJvdW5kKG1lZGlhbihsYXN0X3B5bW50X2FtbnQpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgVmFyaWFuY2UgPSByb3VuZCh2YXIobGFzdF9weW1udF9hbW50KSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgIEVjYXJ0X3R5cGUgPSByb3VuZChzZChsYXN0X3B5bW50X2FtbnQpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgS3VydG9zaXMgPSByb3VuZChrdXJ0b3NpcyhsYXN0X3B5bW50X2FtbnQpLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgU2tld25lc3MgPSByb3VuZChza2V3bmVzcyhsYXN0X3B5bW50X2FtbnQpLCBkaWdpdHMgPSA0KSkKYGBgCgoKIyMjIyBWYXJpYWJsZSA6IEZJQ08gc2NvcmUKLSBmaWNvX3JhbmdlX2hpZ2gKLSBmaWNvX3JhbmdlX2xvdwotIGxhc3RfZmljb19yYW5nZV9oaWdoCi0gbGFzdF9maWNvX3JhbmdlX2xvdwoKTGVzIHNjb3JlcyBkZSBjcsOpZGl0IEZJQ08gc29udCB1bmUgbcOpdGhvZGUgZGUgcXVhbnRpZmljYXRpb24gZXQgZCfDqXZhbHVhdGlvbiBkZSBsYSBzb2x2YWJpbGl0w6kgZCd1bmUgcGVyc29ubmUuIExlcyBzY29yZXMgdm9udCBkZSAzMDAgw6AgODUwLCBjZXV4IGNvbXByaXMgZW50cmUgNjcwIGV0IDczOSDDqXRhbnQgY29uc2lkw6lyw6lzIGNvbW1lIGRlICJib25zIiBhbnTDqWPDqWRlbnRzIGRlIGNyw6lkaXQsIGRlIDc0MCDDoCA3OTkgY29tbWUgZGUgInRyw6hzIGJvbnMiIGFudMOpY8OpZGVudHMgZGUgY3LDqWRpdCBldCBsZXMgc2NvcmVzIHN1cMOpcmlldXJzIMOgIDgwMCBzb250IGNvbnNpZMOpcsOpcyBjb21tZSBkJyAiZXhjZWxsZW50IiBhbnTDqWPDqWRlbnRzIGRlIGNyw6lkaXQuCgpPYnNlcnZhdGlvbiA6IG9uIHJlbWFycXVlIGJpZW4gw6l2aWRlbWVudCBxdWUgcGx1cyBsZSBzY29yZSBkZSBGSUNPIGVzdCDDqWxldsOpLCBlbiBtb3llbm5lLCBwbHVzIGxlIHJpc3F1ZSBkZSBkw6lmYXV0IGVzdCBmYWlibGUuIAoKRGFucyB1biBwcmVtaWVyIHRlbXBzIG9uIHJlZ2FyZGUgbGEgbGltaXRlIGluZsOpcmlldXJlIGRlIGxhIGZvdXJjaGV0dGUgw6AgbGFxdWVsbGUgYXBwYXJ0aWVudCBsZSBGSUNPIGRlIGwnZW1wcnVudGV1ciBhdSBtb21lbnQgZGUgbCdvY3Ryb2kgZHUgcHLDqnQuIE9uIHZvaXQgcXVlIGxhIGRpc3RyaWJ1dGlvbiBkZXMgc2NvcmVzIGVzdCB0csOocyB0csOocyDDqXRhbMOpZSB2ZXJzIGxhIGRyb2l0ZS4gSWwgeSBhIGRvbmMgdW5lIHNrZXduZXNzIGZvcnRlbWVudCBwb3NpdGl2ZSBldCBsYSBwcsOpc2VuY2UgZGUgdmFsZXVycyBleHRyw6ptZW1lbnQgcG9zaXRpdmVzLgoKRGFucyB1biBzZWNvbmQgdGVtcHMsIG9uIHJlZ2FyZGUgbGEgZm91cmNoZXR0ZSBkZSBsaW1pdGVzIMOgIGxhcXVlbGxlIGFwcGFydGllbnQgbGUgZGVybmllciBGSUNPIHRpcsOpIGRlIGwnZW1wcnVudGV1ci4gT24gcmV0cm91dmUgbGEgbcOqbWUgY29uY2x1c2lvbiA6IHBsdXMgbGUgc2NvcmUgZGUgRklDTyBlc3Qgw6lsZXbDqSwgcGx1cyBsZSByaXNxdWUgZGUgZMOpZmF1dCBlc3QgZmFpYmxlIGVuIG1veWVubmUuIENvbmNlcm5hbnQgbGEgZGlzdHJpYnV0aW9uIGRlcyBzY29yZXMsIGVsbGUgc2VtYmxlIHJlbGF0aXZlbWVudCBjZW50csOpZSBzdXIgZGUgImJvbnMiIHNjb3JlIGRlIEZJQ08uCgpgYGB7cn0KZmlnIDwtIHBsb3Rfc3VicGxvdChkYXRhLCBkYXRhJGZpY29fcmFuZ2VfaGlnaCwgZGF0YSRsb2FuX3N0YXR1cywgIkhpZ2ggRklDTyB2YXJpYWJsZSIsICJIaWdoIEZJQ08iLCAiQ291bnQiKQpmaWcKYGBgCgpgYGB7cn0KZmlnIDwtIHBsb3Rfc3VicGxvdChkYXRhLCBkYXRhJGZpY29fcmFuZ2VfbG93LCBkYXRhJGxvYW5fc3RhdHVzLCAiTG93IEZJQ08gdmFyaWFibGUiLCAiTG93IEZJQ08iLCAiQ291bnQiKQpmaWcKYGBgCgpgYGB7cn0KZmlnIDwtIHBsb3Rfc3VicGxvdChkYXRhLCBkYXRhJGxhc3RfZmljb19yYW5nZV9oaWdoLCBkYXRhJGxvYW5fc3RhdHVzLCAiTGFzdCBoaWdoIEZJQ08gdmFyaWFibGUiLCAiTGFzdCBoaWdoIEZJQ08iLCAiQ291bnQiKQpmaWcKYGBgCgpgYGB7cn0KZmlnIDwtIHBsb3Rfc3VicGxvdChkYXRhLCBkYXRhJGxhc3RfZmljb19yYW5nZV9sb3csIGRhdGEkbG9hbl9zdGF0dXMsICJMYXN0IGxvdyBGSUNPIHZhcmlhYmxlIiwgIkxhc3QgbG93IEZJQ08iLCAiQ291bnQiKQpmaWcKYGBgCgoKIyMjIyBWYXJpYWJsZXMgOiAKLSBsYXN0X2ZpY29fcmFuZ2VfaGlnaAotIGxhc3RfZmljb19yYW5nZV9sb3cKLSBncmFkZQotIHN1Yl9ncmFkZQoKT2JzZXJ2YXRpb24gOiBjb21tZSBub3VzIGwnYXR0ZW5kaW9ucywgbWVpbGxldXJlIGVzdCBsYSBub3RlIGRlIGNyw6lkaXQgYWNjb3Jkw6llIMOgIGwnZW1wcnVudGV1ciAoZ3JhZGUgZXQgc3ViLWdyYWRlKSwgbWVpbGxldXIgZXN0IHNvbiBzY29yZSBkZSBGSUNPLiBDZWNpIGVzdCBsb2dpcXVlLCB1biBib24gc2NvcmUgZGUgRklDTyBjb3JyZXNwb25kIG5hdHVyZWxsZW1lbnQgw6AgdW5lIGJvbm5lIG5vdGF0aW9uIGRlIGNyw6lkaXQgcG91ciBsJ2VtcHJ1bnRldXIuCgpgYGB7cn0KZmlnMSA8LSBwbG90X2JveChkYXRhLCBkYXRhJGxhc3RfZmljb19yYW5nZV9oaWdoLCBkYXRhJGdyYWRlLCAiTGFzdCBoaWdoIEZJQ08gdmFyaWFibGUiLCAiTGFzdCBoaWdoIEZJQ08iLCAiQ291bnQiKQpmaWcyIDwtIHBsb3RfYm94KGRhdGEsIGRhdGEkbGFzdF9maWNvX3JhbmdlX2xvdywgZGF0YSRncmFkZSwgIkxhc3QgbG93IEZJQ08gdmFyaWFibGUiLCAiTGFzdCBsb3cgRklDTyIsICJDb3VudCIpCgpmaWcgPC0gc3VicGxvdChmaWcxLCBmaWcyLCBucm93cyA9IDIsIHRpdGxlWSA9IFRSVUUsIHRpdGxlWCA9IFRSVUUsIG1hcmdpbiA9IDAuMSkKZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gIkxhc3QgRklDTyBieSBncmFkZSIsCiAgICAgICAgIHBsb3RfYmdjb2xvcj0iI0ZGRkZGRiIsIAogICAgICAgICB4YXhpcyA9IGxpc3QoCiAgICAgICAgICAgemVyb2xpbmVjb2xvciA9ICIjZmZmZiIsIAogICAgICAgICAgIHplcm9saW5ld2lkdGggPSAyLCAKICAgICAgICAgICBncmlkY29sb3IgPSAiZmZmZiIpLCAKICAgICAgICAgeWF4aXMgPSBsaXN0KAogICAgICAgICAgIHplcm9saW5lY29sb3IgPSAiI2ZmZmYiLCAKICAgICAgICAgICB6ZXJvbGluZXdpZHRoID0gMiwgCiAgICAgICAgICAgZ3JpZGNvbG9yID0gImZmZmYiKSwgCiAgICAgICAgIHNob3dsZWdlbmQgPSBGQUxTRSwKICAgICAgICAgc2hvd2xlZ2VuZDIgPSBGQUxTRSkKCmZpZwpgYGAKCmBgYHtyfQpmaWcxIDwtIHBsb3RfYm94KGRhdGEsIGRhdGEkbGFzdF9maWNvX3JhbmdlX2hpZ2gsIGRhdGEkc3ViX2dyYWRlLCAiTGFzdCBoaWdoIEZJQ08gdmFyaWFibGUiLCAiTGFzdCBoaWdoIEZJQ08iLCAiQ291bnQiKQpmaWcyIDwtIHBsb3RfYm94KGRhdGEsIGRhdGEkbGFzdF9maWNvX3JhbmdlX2xvdywgZGF0YSRzdWJfZ3JhZGUsICJMYXN0IGxvdyBGSUNPIHZhcmlhYmxlIiwgIkxhc3QgbG93IEZJQ08iLCAiQ291bnQiKQoKZmlnIDwtIHN1YnBsb3QoZmlnMSwgZmlnMiwgbnJvd3MgPSAyLCB0aXRsZVkgPSBUUlVFLCB0aXRsZVggPSBUUlVFLCBtYXJnaW4gPSAwLjEpCmZpZyA8LSBmaWcgJT4lIGxheW91dCh0aXRsZSA9ICJMYXN0IEZJQ08gYnkgZ3JhZGUiLAogICAgICAgICBwbG90X2JnY29sb3I9IiNGRkZGRkYiLCAKICAgICAgICAgeGF4aXMgPSBsaXN0KAogICAgICAgICAgIHplcm9saW5lY29sb3IgPSAiI2ZmZmYiLCAKICAgICAgICAgICB6ZXJvbGluZXdpZHRoID0gMiwgCiAgICAgICAgICAgZ3JpZGNvbG9yID0gImZmZmYiKSwgCiAgICAgICAgIHlheGlzID0gbGlzdCgKICAgICAgICAgICB6ZXJvbGluZWNvbG9yID0gIiNmZmZmIiwgCiAgICAgICAgICAgemVyb2xpbmV3aWR0aCA9IDIsIAogICAgICAgICAgIGdyaWRjb2xvciA9ICJmZmZmIiksIAogICAgICAgICBzaG93bGVnZW5kID0gRkFMU0UsCiAgICAgICAgIHNob3dsZWdlbmQyID0gRkFMU0UpCgpmaWcKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFN5bnRow6hzZSBkZSBsJ0VEQQoKIyMjIyBDYXJhY3TDqXJpc3RpcXVlcyBwb3RlbnRpZWxsZW1lbnQgaW50w6lyZXNzYW50ZXMKYGBge3J9CmRhdGEzIDwtIHN1YnNldChkYXRhMiwgc2VsZWN0ID0gYygiZGVmYXVsdCIsICJsb2FuX2FtbnQiLCAiZnVuZGVkX2FtbnQiLCAiaW50X3JhdGUiLCAiaW5zdGFsbG1lbnQiLCAiYW5udWFsX2luYyIsICJkZWxpbnFfMnlycyIsICJpbnFfbGFzdF82bXRocyIsICJtdGhzX3NpbmNlX2xhc3RfZGVsaW5xIiwgIm9wZW5fYWNjIiwgInRvdGFsX2FjYyIsICJmaWNvX3JhbmdlX2hpZ2giLCAidGVybSIsICJncmFkZSIsICJob21lX293bmVyc2hpcCIsICJ2ZXJpZmljYXRpb25fc3RhdHVzIiwgInB1cnBvc2UiLCAiYWRkcl9zdGF0ZSIsICJhcHBsaWNhdGlvbl90eXBlIiwgInB1Yl9yZWNfYmFua3J1cHRjaWVzIikpCgpzdHIoZGF0YTMpCmBgYAoKClLDqWFsaXNlciBkZXMgZ3JhcGhpcXVlcyBkZSBkZW5zaXTDqSBhdmVjIEtERSBub3VzIHBlcm1ldCBkZSB2cmFpbWVudCB2aXN1YWxpc2VyIGxhIGRpc3RpbmN0aW9uIGludGVyLWNsYXNzZS4gUG91ciBsZXMgdmFyaWFibGVzIGNvbnRpbnVlcywgb24gcmVtYXJxdWUgYmllbiBxdWUgY2VydGFpbmVzIGNhcmFjdMOpcmlzdGlxdWVzIGFwcG9ydGVudCB1bmUgYm9ubmUgaW5mb3JtYXRpb24uIFF1ZWxxdWVzIGV4ZW1wbGVzIDogCi0gTGUgc2NvcmUgZGUgRklDTywgYydlc3QgYXNzZXogbMOpZ2VyIG1haXMgbm91cyB2b3lvbnMgYmllbiBxdWUgcG91ciBsZXMgZW1wcnVudGV1cnMgbifDqXRhbnQgcGFzIGVuIGTDqWZhdXQgbGVzIHNjb3JlcyBvbnQgdGVuZGFuY2Ugw6Agw6p0cmUgc3Vww6lyaWV1ci4gT24gdm9pdCBtw6ptZSBxdWUgcG91ciBsYSBjbGFzc2UgVFJVRSBpbCB5IGEgdW4gcGljIGQnb2JzZXJ2YXRpb24gYXUgZMOpYnV0IGRlIGxhIGRpc3RyaWJ1dGlvbiBjZSBxdWkgbW9udHJlIHF1J3VuZSBncm9zc2UgcGFyIGRlcyBlbXBydW50ZXVycyBlbiBkw6lmYXV0IHBvc3PDqGRlbnQgZGUgZmFpYmxlcyBzY29yZXMgZGUgRklDTyA7Ci0gTGEgdmFyaWFibGUgZHUgdGF1eCBkJ2ludMOpcsOqdCBwZXJtZXQgYXVzc2kgZGUgYmllbiBtb250cmVyIGxlIGNsaXZhZ2UgZW50cmUgbGVzIGRldXggY2xhc3NlcyBhdmVjIGRlcyB0YXV4IHF1aSBzb250IHBsdXMgw6lsZXbDqXMgcG91ciBsZXMgZW1wcnVudGV1cnMgZW4gZMOpZmF1dCA7Ci0gTGVzIGVtcHJ1bnRldXJzIGVuIGTDqWZhaWxsYW5jZSBvbnQgw6lnYWxlbWVudCB0ZW5kYW5jZSDDoCBlbXBydW50ZXIgZGUgcGx1cyBncm9zIG1vbnRhbnRzLCBsYSBtYWpvcml0w6kgZGVzIGVtcHJ1bnRldXJzIHNhaW5zIGVtcHJ1bnRlbnQgcXVhc2ltZW50IGxlIG1vbnRhbnQgbW95ZW4gb2N0cm95w6kgKDEwIC8gMTJrICQpIDsKLSBMZSBub21icmUgdG90YWwgZGUgbGlnbmVzIGRlIGNyw6lkaXQgb3V2ZXJ0ZXMgbidlc3QgcGFzIHZyYWltZW50IGRpc2NyaW1pbmFudCBhZmluIGRlIGRpc3NvY2llciBsZXMgZGV1eCBjbGFzc2VzIGNvbW1lIG5vdXMgbCdhdmlvbnMgbm90aWZpw6kgcHLDqWPDqWRlbW1lbnQgbG9ycyBkZSBsJ2FuYWx5c2UgZGUgbGEgY2FyYWN0w6lyaXN0aXF1ZSA7Ci0gTm90b25zIMOpZ2FsZW1lbnQgcXVlIGxlcyBjYXJhY3TDqXJpc3RpcXVlcyAibG9hbl9hbW50IiBldCAiZnVuZGVkX2FtbnQiIG9udCB1bmUgZGlzdHJpYnV0aW9uIGRlIHZhbGV1cnMgdHLDqHMgc2ltaWxhaXJlcyBjZSBxdWkgaW5kaXF1ZXJhaXQgdW5lIGZvcnRlIHJlZG9uZGFuY2UgZCdpbmZvcm1hdGlvbiBkb25jIHVuZSBtdXRsaS1jb2xpbsOpYXJpdMOpIGltcGFyZmFpdGUgw6Agc3VwcHJpbWVyIHBvdXIgbmUgcGFzIGJpYWlzZXIgbm90cmUgZnV0dXJlIG1vZMOpbGlzYXRpb24uCgojIyMjIFBsb3QgZGVzIGNhcmFjdMOpcmlzdGlxdWVzIHF1YW50aXRhdGl2ZXMgaW50w6lyZXNzYW50ZXMKYGBge3J9Cm51bV92YXJzIDwtIGRhdGEzICU+JSBzYXBwbHkoaXMubnVtZXJpYykgJT4lIHdoaWNoKCkgJT4lIG5hbWVzKCkKCmRhdGEzICU+JQogIHNlbGVjdF8oLmRvdHMgPSBudW1fdmFycykgJT4lCiAgZ2F0aGVyKG1lYXN1cmUsIHZhbHVlKSAlPiUKICBtdXRhdGUoZGVmYXVsdCA9IGZhY3RvcihyZXAoeCA9IGRhdGEzJGRlZmF1bHQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZW5ndGgub3V0ID0gbGVuZ3RoKG51bV92YXJzKSAqIGRpbShkYXRhMylbMV0pLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJUUlVFIiwgIkZBTFNFIikpKSAlPiUKICBnZ3Bsb3QoZGF0YSA9IC4sIGFlcyh4ID0gdmFsdWUsIGZpbGwgPSBkZWZhdWx0LCAKICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGRlZmF1bHQsIG9yZGVyID0gLWRlZmF1bHQpKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC4zLCBzaXplID0gMC41KSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikgKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDEiKSArCiAgZmFjZXRfd3JhcCggfiBtZWFzdXJlLCBzY2FsZXMgPSAiZnJlZSIsIG5jb2wgPSAzKQpgYGAKCgpDb25jZXJuYW50IGxlcyB2YXJpYWJsZXMgY2F0w6lnb3JpZWxsZXMgOgotIExlcyBkZW1hbmRlcyBkZSBwcsOqdCBzb250IGfDqW7DqXJhbGVtZW50IHBvdXIgdW4gZW1wcnVudCBzZXVsIGV0IHBhcyBjb25qb2ludCA7Ci0gTGEgcHJvcG9ydGlvbiBkJ2VtcHJ1bnRldXIgZW4gZMOpZmF1dCBlc3Qgw6lsZXbDqWUgZGFucyBsZXMgZ3JhZGVzIEIsIEMgZXQgRCA7Ci0gTGEgcHJvcG9ydGlvbiBkJ2VtcHJ1bnRldXIgZW4gZMOpZmF1dCBlc3Qgw6lsZXbDqWUgcG91ciBsZXMgZW1wcnVudGV1cnMgZW4gaHlwb3Row6hxdWUgb3UgZW4gbG9jYXRpb24gaW1tb2JpbGnDqHJlIDsKLSBEZSBub21icmV1eCBlbXBydW50ZXVycyBkw6lmYWlsbGFudHMgb250IG9idGVudSB1biBwcsOqdCBwb3VyIGxlIG1vdGlmIGRlIGxhIGNvbnNvbGlkYXRpb24gZGUgZGV0dGUgdHLDqHMgcHJpbmNpcGFsZW1lbnQgOwotIEwnw6ljaMOpYW5jZSBkZSBsJ2VtcHJ1bnQgZXQgbGEgdsOpcmlmaWNhdGlvbiBkdSBzdGF0dXQgbmUgcGVybWV0dGVudCBwYXMgZGUgY29uZm9uZHJlIGxlcyBkZXV4IHR5cGVzIGQnZW1wcnVudGV1ciBjYXIgbGVzIG1vZGFsaXTDqXMgZGVzIHZhcmlhYmxlcyBhZmZpY2hlbnQgbGVzIG3Dqm1lcyBwcm9wb3J0aW9ucy4KCiMjIyMgUGxvdCBkZXMgY2FyYWN0w6lyaXN0aXF1ZXMgcXVhbGl0YXRpdmVzIGludMOpcmVzc2FudGVzCmBgYHtyfQpjaGFyX3ZhcnMgPC0gZGF0YTMgJT4lIHNhcHBseShpcy5jaGFyYWN0ZXIpICU+JSB3aGljaCgpICU+JSBuYW1lcygpCgpkYXRhMyAlPiUKICBzZWxlY3RfKC5kb3RzID0gY2hhcl92YXJzKSAlPiUKICBnYXRoZXIobWVhc3VyZSwgdmFsdWUpICU+JQogIG11dGF0ZShkZWZhdWx0ID0gZmFjdG9yKHJlcCh4ID0gZGF0YTMkZGVmYXVsdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlbmd0aC5vdXQgPSBsZW5ndGgoY2hhcl92YXJzKSAqIGRpbShkYXRhMylbMV0pLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJUUlVFIiwgIkZBTFNFIikpKSAlPiUKICBnZ3Bsb3QoZGF0YSA9IC4sIGFlcyh4ID0gdmFsdWUsIGZpbGwgPSBkZWZhdWx0LCAKICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGRlZmF1bHQsIG9yZGVyID0gLWRlZmF1bHQpKSArCiAgZ2VvbV9oaXN0b2dyYW0oc3RhdCA9ICJjb3VudCIsIGFscGhhID0gMC41KSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikgKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDEiKSArCiAgZmFjZXRfd3JhcCggfiBtZWFzdXJlLCBzY2FsZXMgPSAiZnJlZSIsIG5jb2wgPSAzKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCg==